├── .gitignore ├── CREDITS.txt ├── LICENSE.txt ├── MANIFEST.in ├── README.txt ├── docs ├── Makefile ├── api.rst ├── conf.py ├── index.rst ├── installation.rst ├── overview.rst └── usage │ ├── additional_groups.rst │ ├── index.rst │ └── simple.rst ├── permissions ├── __init__.py ├── admin.py ├── backend.py ├── exceptions.py ├── fixtures │ └── initial.xml ├── locale │ └── de │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── templatetags │ ├── __init__.py │ └── permissions_tags.py ├── tests.py └── utils.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | .installed.cfg 3 | *.pyc 4 | *.pyo 5 | *.DS_Store 6 | *develop-eggs/ 7 | *.egg-info/ 8 | *eggs/ 9 | *parts/ 10 | *bin/ 11 | *downloads/ 12 | *docs/_build 13 | *dist -------------------------------------------------------------------------------- /CREDITS.txt: -------------------------------------------------------------------------------- 1 | ======= 2 | CREDITS 3 | ======= 4 | 5 | ifhasperm 6 | ========= 7 | 8 | The template tag ifhasperm is ripped off and adapted from django-authority 9 | from Jannis Leidel. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Kai Diefenbach - IQ++ 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of other contributors may 15 | be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include CREDITS.txt 2 | include LICENSE.txt 3 | include MANIFEST.txt 4 | include README.txt 5 | recursive-include permissions/locale * 6 | recursive-include permissions/fixtures * -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | django-permissions provides per-object permissions for Django on roles: http://en.wikipedia.org/wiki/Role-based_access_control 5 | 6 | Code 7 | ==== 8 | 9 | The code can be found on bitbucket: http://github.com/diefenbach/django-permissions 10 | 11 | Implementations 12 | =============== 13 | 14 | If you want to see a comprehensive implementation of django-permissions take 15 | a look at the CMS `LFC `_ 16 | 17 | Changes 18 | ======= 19 | 20 | 1.2.2 (2014-05-10) 21 | ------------------ 22 | 23 | * Fixes caching of roles 24 | 25 | 1.2 (2014-05-10) 26 | ---------------- 27 | 28 | * Adds Django 1.8 support 29 | * Adds initial migratinons 30 | 31 | 1.1 (2014-06-26) 32 | ---------------- 33 | 34 | * Fixes deprecation warning 35 | * Removes raw SQL statements 36 | * Caches calculated roles on user object 37 | 38 | 1.0.3 (2011-04-30) 39 | ------------------ 40 | 41 | Bugfix release 42 | 43 | * Bugfix: Removed mutuable parameters; issue #11 44 | 45 | Changes 46 | ======= 47 | 48 | 1.0.2 (2011-04-09) 49 | ------------------ 50 | 51 | Bugfix release 52 | 53 | * Bugfix: prevent to add same Users several times to a Role; issue #6 of django-workflows 54 | * Updated Development Status to "5 - Production/Stable" 55 | 56 | 1.0.1 (2011-04-08) 57 | ------------------ 58 | 59 | Bugfix release 60 | 61 | * Bugfix: DatabaseErrors with Postgres; issue #5. 62 | * Bugfix: changed order of passed parameters to has_permission; issue #6 63 | * Bugfix: removed not needed import of "sets"; issue #8 64 | 65 | 1.0 (2010-08-24) 66 | ---------------- 67 | 68 | * First final release 69 | 70 | 1.0 beta 4 (2010-07-23) 71 | ----------------------- 72 | 73 | * Added check_permission method to PermissionBase 74 | * Added Unauthorized exception 75 | 76 | 1.0 beta 3 (2010-07-07) 77 | ----------------------- 78 | 79 | * Bugfix get_users/get_groups method of class Role; issue #2 80 | * Bugfix: check for an object before trying to add local role; issue #3 81 | * Bugfix: registration of permissions for specific content types only 82 | 83 | 1.0 beta 2 (2010-05-17) 84 | ----------------------- 85 | 86 | * Added license 87 | 88 | 1.0 beta 1 (2010-05-17) 89 | ----------------------- 90 | 91 | * Bugfix has_permission. Using roles=None instead of roles=[]. 92 | 93 | 1.0 alpha 4 (2010-04-16) 94 | ------------------------ 95 | 96 | * Moved PermissionBase to __init__.py 97 | 98 | 1.0 alpha 3 (2010-03-30) 99 | ------------------------ 100 | 101 | * Added roles 102 | 103 | 1.0 alpha 2 (2010-03-22) 104 | ------------------------ 105 | 106 | * Added a lot of improvements on several places 107 | 108 | 1.0 alpha 1 (2010-03-11) 109 | ------------------------ 110 | 111 | * Initial public release 112 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " pickle to make pickle files" 22 | @echo " json to make JSON files" 23 | @echo " htmlhelp to make HTML files and a HTML help project" 24 | @echo " qthelp to make HTML files and a qthelp project" 25 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 26 | @echo " changes to make an overview of all changed/added/deprecated items" 27 | @echo " linkcheck to check all external links for integrity" 28 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 29 | 30 | clean: 31 | -rm -rf $(BUILDDIR)/* 32 | 33 | html: 34 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 35 | @echo 36 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 37 | 38 | dirhtml: 39 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 40 | @echo 41 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 42 | 43 | pickle: 44 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 45 | @echo 46 | @echo "Build finished; now you can process the pickle files." 47 | 48 | json: 49 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 50 | @echo 51 | @echo "Build finished; now you can process the JSON files." 52 | 53 | htmlhelp: 54 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 55 | @echo 56 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 57 | ".hhp project file in $(BUILDDIR)/htmlhelp." 58 | 59 | qthelp: 60 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 61 | @echo 62 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 63 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 64 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-permissions.qhcp" 65 | @echo "To view the help file:" 66 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-permissions.qhc" 67 | 68 | latex: 69 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 70 | @echo 71 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 72 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 73 | "run these through (pdf)latex." 74 | 75 | changes: 76 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 77 | @echo 78 | @echo "The overview file is in $(BUILDDIR)/changes." 79 | 80 | linkcheck: 81 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 82 | @echo 83 | @echo "Link check complete; look for any errors in the above output " \ 84 | "or in $(BUILDDIR)/linkcheck/output.txt." 85 | 86 | doctest: 87 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 88 | @echo "Testing of doctests in the sources finished, look at the " \ 89 | "results in $(BUILDDIR)/doctest/output.txt." 90 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | === 2 | API 3 | === 4 | 5 | .. automodule:: permissions.utils 6 | 7 | Utils 8 | ===== 9 | 10 | Manage permissions 11 | ------------------ 12 | 13 | .. autofunction:: grant_permission 14 | .. autofunction:: remove_permission 15 | .. autofunction:: has_permission 16 | .. autofunction:: reset 17 | 18 | Manage roles 19 | ------------ 20 | 21 | .. autofunction:: add_role 22 | .. autofunction:: add_local_role 23 | 24 | .. autofunction:: get_roles 25 | .. autofunction:: get_global_roles 26 | .. autofunction:: get_local_roles 27 | 28 | .. autofunction:: remove_role 29 | .. autofunction:: remove_local_role 30 | 31 | .. autofunction:: remove_roles 32 | .. autofunction:: remove_local_roles 33 | 34 | Manage inheritance 35 | ------------------ 36 | 37 | .. autofunction:: add_inheritance_block 38 | .. autofunction:: remove_inheritance_block 39 | .. autofunction:: is_inherited 40 | 41 | Registration 42 | ------------ 43 | 44 | Register permissions 45 | ^^^^^^^^^^^^^^^^^^^^ 46 | 47 | .. autofunction:: register_permission 48 | .. autofunction:: unregister_permission 49 | 50 | Register roles 51 | ^^^^^^^^^^^^^^ 52 | 53 | .. autofunction:: register_role 54 | .. autofunction:: unregister_role 55 | 56 | Register groups 57 | ^^^^^^^^^^^^^^^ 58 | 59 | .. autofunction:: register_group 60 | .. autofunction:: unregister_group 61 | 62 | Helpers 63 | ------- 64 | 65 | .. autofunction:: get_user 66 | .. autofunction:: get_group 67 | .. autofunction:: get_role 68 | 69 | Template tags 70 | ============= 71 | 72 | **ifhasperm** 73 | 74 | Checks whether the current user has passed permission:: 75 | 76 | {% ifhasperm view %} 77 | Has permission 78 | {% else %} 79 | Doesn't have permission 80 | {% endifhasperm %} 81 | 82 | Models 83 | ====== 84 | 85 | .. autoclass:: permissions.PermissionBase 86 | :members: 87 | 88 | .. autoclass:: permissions.models.Permission 89 | :members: 90 | 91 | .. autoclass:: permissions.models.ObjectPermission 92 | :members: 93 | 94 | .. autoclass:: permissions.models.ObjectPermissionInheritanceBlock 95 | :members: 96 | 97 | .. autoclass:: permissions.models.Role 98 | :members: 99 | 100 | .. autoclass:: permissions.models.PrincipalRoleRelation 101 | :members: 102 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # django-permissions documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Mar 11 07:25:51 2010. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.append(os.path.abspath('.')) 20 | 21 | sys.path.append(os.path.abspath("../../../project")) 22 | sys.path.append(os.path.abspath("../../../parts/permissions")) 23 | 24 | # -- General configuration ----------------------------------------------------- 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be extensions 27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 28 | extensions = ['sphinx.ext.autodoc'] 29 | 30 | # Add any paths that contain templates here, relative to this directory. 31 | templates_path = ['_templates'] 32 | 33 | # The suffix of source filenames. 34 | source_suffix = '.rst' 35 | 36 | # The encoding of source files. 37 | #source_encoding = 'utf-8' 38 | 39 | # The master toctree document. 40 | master_doc = 'index' 41 | 42 | # General information about the project. 43 | project = u'django-permissions' 44 | copyright = u'2010, Kai Diefenbach' 45 | 46 | # The version info for the project you're documenting, acts as replacement for 47 | # |version| and |release|, also used in various other places throughout the 48 | # built documents. 49 | # 50 | # The short X.Y version. 51 | version = '1.0' 52 | # The full version, including alpha/beta/rc tags. 53 | release = '1.0' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of documents that shouldn't be included in the build. 66 | #unused_docs = [] 67 | 68 | # List of directories, relative to source directory, that shouldn't be searched 69 | # for source files. 70 | exclude_trees = ['_build'] 71 | 72 | # The reST default role (used for this markup: `text`) to use for all documents. 73 | #default_role = None 74 | 75 | # If true, '()' will be appended to :func: etc. cross-reference text. 76 | #add_function_parentheses = True 77 | 78 | # If true, the current module name will be prepended to all description 79 | # unit titles (such as .. function::). 80 | #add_module_names = True 81 | 82 | # If true, sectionauthor and moduleauthor directives will be shown in the 83 | # output. They are ignored by default. 84 | #show_authors = False 85 | 86 | # The name of the Pygments (syntax highlighting) style to use. 87 | pygments_style = 'sphinx' 88 | 89 | # A list of ignored prefixes for module index sorting. 90 | #modindex_common_prefix = [] 91 | 92 | 93 | # -- Options for HTML output --------------------------------------------------- 94 | 95 | # The theme to use for HTML and HTML Help pages. Major themes that come with 96 | # Sphinx are currently 'default' and 'sphinxdoc'. 97 | html_theme = 'default' 98 | 99 | # Theme options are theme-specific and customize the look and feel of a theme 100 | # further. For a list of options available for each theme, see the 101 | # documentation. 102 | #html_theme_options = {} 103 | 104 | # Add any paths that contain custom themes here, relative to this directory. 105 | #html_theme_path = [] 106 | 107 | # The name for this set of Sphinx documents. If None, it defaults to 108 | # " v documentation". 109 | #html_title = None 110 | 111 | # A shorter title for the navigation bar. Default is the same as html_title. 112 | #html_short_title = None 113 | 114 | # The name of an image file (relative to this directory) to place at the top 115 | # of the sidebar. 116 | #html_logo = None 117 | 118 | # The name of an image file (within the static path) to use as favicon of the 119 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 120 | # pixels large. 121 | #html_favicon = None 122 | 123 | # Add any paths that contain custom static files (such as style sheets) here, 124 | # relative to this directory. They are copied after the builtin static files, 125 | # so a file named "default.css" will overwrite the builtin "default.css". 126 | html_static_path = ['_static'] 127 | 128 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 129 | # using the given strftime format. 130 | #html_last_updated_fmt = '%b %d, %Y' 131 | 132 | # If true, SmartyPants will be used to convert quotes and dashes to 133 | # typographically correct entities. 134 | #html_use_smartypants = True 135 | 136 | # Custom sidebar templates, maps document names to template names. 137 | #html_sidebars = {} 138 | 139 | # Additional templates that should be rendered to pages, maps page names to 140 | # template names. 141 | #html_additional_pages = {} 142 | 143 | # If false, no module index is generated. 144 | #html_use_modindex = True 145 | 146 | # If false, no index is generated. 147 | #html_use_index = True 148 | 149 | # If true, the index is split into individual pages for each letter. 150 | #html_split_index = False 151 | 152 | # If true, links to the reST sources are added to the pages. 153 | #html_show_sourcelink = True 154 | 155 | # If true, an OpenSearch description file will be output, and all pages will 156 | # contain a tag referring to it. The value of this option must be the 157 | # base URL from which the finished HTML is served. 158 | #html_use_opensearch = '' 159 | 160 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 161 | #html_file_suffix = '' 162 | 163 | # Output file base name for HTML help builder. 164 | htmlhelp_basename = 'django-permissionsdoc' 165 | 166 | 167 | # -- Options for LaTeX output -------------------------------------------------- 168 | 169 | # The paper size ('letter' or 'a4'). 170 | #latex_paper_size = 'letter' 171 | 172 | # The font size ('10pt', '11pt' or '12pt'). 173 | #latex_font_size = '10pt' 174 | 175 | # Grouping the document tree into LaTeX files. List of tuples 176 | # (source start file, target name, title, author, documentclass [howto/manual]). 177 | latex_documents = [ 178 | ('index', 'django-permissions.tex', u'django-permissions Documentation', 179 | u'Kai Diefenbach', 'manual'), 180 | ] 181 | 182 | # The name of an image file (relative to this directory) to place at the top of 183 | # the title page. 184 | #latex_logo = None 185 | 186 | # For "manual" documents, if this is true, then toplevel headings are parts, 187 | # not chapters. 188 | #latex_use_parts = False 189 | 190 | # Additional stuff for the LaTeX preamble. 191 | #latex_preamble = '' 192 | 193 | # Documents to append as an appendix to all manuals. 194 | #latex_appendices = [] 195 | 196 | # If false, no module index is generated. 197 | #latex_use_modindex = True 198 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | django-permissions 2 | ================== 3 | 4 | django-permissions is a generic framework for per-object permissions for Django based on roles: http://en.wikipedia.org/wiki/Role-based_access_control 5 | 6 | Contents 7 | ======== 8 | 9 | .. toctree:: 10 | :maxdepth: 1 11 | 12 | overview.rst 13 | installation.rst 14 | usage/index.rst 15 | api.rst 16 | 17 | Indices and tables 18 | ================== 19 | 20 | * :ref:`genindex` 21 | * :ref:`modindex` 22 | * :ref:`search` 23 | 24 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | To install django-permission, proceed as following: 6 | 7 | 1. Easy install it: ``$ easy_install django-permissions`` 8 | 2. Add `permissions` to `INSTALLED_APPS` within `settings.py` of your django 9 | project. 10 | 3. Sync your database: ``$ django-admin syncdb`` -------------------------------------------------------------------------------- /docs/overview.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Overview 3 | ======== 4 | 5 | * django-permissions is a generic framework for per-object permissions for 6 | Django based on roles: http://en.wikipedia.org/wiki/Role-based_access_control 7 | 8 | Permissions 9 | =========== 10 | 11 | * Permissions are granted to roles (and only to roles) in order to allow 12 | something to users or groups which have these roles. 13 | 14 | Roles 15 | ===== 16 | 17 | * Roles are used to grant permissions. Typical roles are *Reader*, *Manager* 18 | or *Editor*. 19 | 20 | Local Roles 21 | =========== 22 | 23 | * Local roles are roles which are assigned to users and groups for specific 24 | content objects. 25 | 26 | Users 27 | ===== 28 | 29 | * Users are actors which may need a permission to do something within the 30 | system. 31 | * Users can be member of several groups. 32 | * User can have several roles, directly or via a membership to a group 33 | (these are considered as global). 34 | * User can have local roles, directly or via a membership to a group. That is 35 | roles for a specific object. 36 | * Users have all roles of their groups - global and local ones. 37 | * Users have all permissions of their roles - global and local ones. 38 | 39 | Groups 40 | ====== 41 | 42 | * Groups combines users together. 43 | * Groups can have roles (these are considered as global). 44 | * Groups can have local roles, that is roles for a specific object. 45 | * Groups has all permissions of their roles - global and local ones. 46 | * Users of a Group have the group's roles and permissions. -------------------------------------------------------------------------------- /docs/usage/additional_groups.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | Additional groups 3 | ================= 4 | 5 | This will demonstrate how one can create special groups (per convention) and 6 | check permissions against them even if the user has not been assigned to these 7 | groups explicitly. 8 | 9 | Create a new user 10 | ----------------- 11 | 12 | .. code-block:: python 13 | 14 | >>> from django.contrib.auth.models import User 15 | >>> user = User.objects.create(username="doe") 16 | 17 | Create new permissions 18 | ---------------------- 19 | 20 | .. code-block:: python 21 | 22 | >>> from permissions.utils import register_permission 23 | >>> permission = register_permission("View", "view") 24 | >>> permission = register_permission("Edit", "edit") 25 | 26 | Create new role 27 | --------------- 28 | 29 | .. code-block:: python 30 | 31 | >>> from permissions.utils import register_role 32 | >>> anonymous = register_role("Anonymous") 33 | >>> owner = register_role("Owner") 34 | 35 | This will create default Django groups. 36 | 37 | Create a content object 38 | ----------------------- 39 | 40 | .. code-block:: python 41 | 42 | >>> from django.contrib.flatpages.models import FlatPage 43 | >>> content = FlatPage.objects.create(title="Example", url="example") 44 | >>> content.creator = user 45 | 46 | Grant permissions 47 | ----------------- 48 | 49 | .. code-block:: python 50 | 51 | >>> from permissions.utils import grant_permission 52 | >>> grant_permission(content, anonymous, "view") 53 | >>> grant_permission(content, owner, "edit") 54 | 55 | Now all users which are member of the special group "Anonymous" have the 56 | permission to view the object "content". And all users which are member of the 57 | special group "Owner" have the permission to edit the content. 58 | 59 | Check permission 60 | ---------------- 61 | 62 | .. code-block:: python 63 | 64 | >>> from permissions.utils import has_permission 65 | 66 | # Every user is automatically within the Anonymous group. 67 | >>> roles = [anonymous] 68 | 69 | # The creator of the page is also within the Owner group. 70 | # Note: FlatPages actually don't have a creator attribute. 71 | >>> if user == content.creator: 72 | ... roles.append(owner) 73 | 74 | # Passing the additional groups to has_permission 75 | >>> has_permission(content, user, "edit", roles) 76 | True 77 | 78 | >>> has_permission(content, user, "view", roles) 79 | True 80 | 81 | More information 82 | ---------------- 83 | 84 | .. seealso:: 85 | 86 | This is just a simple use case. Look into the :doc:`API documentation <../api>` for more. -------------------------------------------------------------------------------- /docs/usage/index.rst: -------------------------------------------------------------------------------- 1 | Usage 2 | ===== 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | simple.rst 10 | additional_groups.rst -------------------------------------------------------------------------------- /docs/usage/simple.rst: -------------------------------------------------------------------------------- 1 | ====== 2 | Simple 3 | ====== 4 | 5 | Create a new user 6 | ----------------- 7 | 8 | .. code-block:: python 9 | 10 | >>> from django.contrib.auth.models import User 11 | >>> user = User.objects.create(username="doe") 12 | 13 | Create a new permission 14 | ----------------------- 15 | 16 | .. code-block:: python 17 | 18 | >>> from permissions.utils import register_permission 19 | >>> permission = register_permission("View", "view") 20 | 21 | Create a new role 22 | ----------------- 23 | 24 | .. code-block:: python 25 | 26 | >>> from permissions.utils import register_role 27 | >>> editor = register_role("Editor") 28 | 29 | Assign user to role 30 | ------------------- 31 | 32 | .. code-block:: python 33 | 34 | >>> editor.add_principal(user) 35 | 36 | Create a content object 37 | ----------------------- 38 | 39 | .. code-block:: python 40 | 41 | >>> from django.contrib.flatpages.models import FlatPage 42 | >>> content = FlatPage.objects.create(title="Example", url="example") 43 | 44 | Grant permission 45 | ---------------- 46 | 47 | .. code-block:: python 48 | 49 | >>> from permissions.utils import grant_permission 50 | >>> grant_permission(content, editor, "view") 51 | 52 | Now all users which are member of the role "Editor" have the permission to 53 | view object "content". 54 | 55 | Check permission 56 | ---------------- 57 | 58 | .. code-block:: python 59 | 60 | >>> from permissions.utils import has_permission 61 | >>> has_permission(content, user, "view") 62 | True 63 | 64 | This will check whether the current user has the permission "View" for the 65 | FlatPage "content". 66 | 67 | More information 68 | ---------------- 69 | 70 | .. seealso:: 71 | 72 | This is just a simple use case. Look into the :doc:`API documentation <../api>` for more. -------------------------------------------------------------------------------- /permissions/__init__.py: -------------------------------------------------------------------------------- 1 | # permissions imports 2 | import permissions.utils 3 | from permissions.exceptions import Unauthorized 4 | 5 | class PermissionBase(object): 6 | """Mix-in class for permissions. 7 | """ 8 | def grant_permission(self, role, permission): 9 | """Grants passed permission to passed role. Returns True if the 10 | permission was able to be added, otherwise False. 11 | 12 | **Parameters:** 13 | 14 | role 15 | The role for which the permission should be granted. 16 | 17 | permission 18 | The permission which should be granted. Either a permission 19 | object or the codename of a permission. 20 | """ 21 | return permissions.utils.grant_permission(self, role, permission) 22 | 23 | def remove_permission(self, role, permission): 24 | """Removes passed permission from passed role. Returns True if the 25 | permission has been removed. 26 | 27 | **Parameters:** 28 | 29 | role 30 | The role for which a permission should be removed. 31 | 32 | permission 33 | The permission which should be removed. Either a permission object 34 | or the codename of a permission. 35 | """ 36 | return permissions.utils.remove_permission(self, role, permission) 37 | 38 | def has_permission(self, user, permission, roles=None): 39 | """Returns True if the passed user has passed permission for this 40 | instance. Otherwise False. 41 | 42 | **Parameters:** 43 | 44 | permission 45 | The permission's codename which should be checked. Must be a 46 | string with a valid codename. 47 | 48 | user 49 | The user for which the permission should be checked. 50 | 51 | roles 52 | If passed, these roles will be assigned to the user temporarily 53 | before the permissions are checked. 54 | """ 55 | if roles is None: 56 | roles = [] 57 | return permissions.utils.has_permission(self, user, permission, roles) 58 | 59 | def check_permission(self, user, permission, roles=None): 60 | """Raise Unauthorized if the the passed user hasn't passed permission 61 | for this instance. 62 | 63 | **Parameters:** 64 | 65 | permission 66 | The permission's codename which should be checked. Must be a 67 | string with a valid codename. 68 | 69 | user 70 | The user for which the permission should be checked. 71 | 72 | roles 73 | If passed, these roles will be assigned to the user temporarily 74 | before the permissions are checked. 75 | """ 76 | if roles is None: 77 | roles = [] 78 | 79 | if not self.has_permission(user, permission, roles): 80 | raise Unauthorized("User '%s' doesn't have permission '%s' for object '/%s' (%s)." % (user, permission, self.slug, self.__class__.name)) 81 | 82 | def add_inheritance_block(self, permission): 83 | """Adds an inheritance block for the passed permission. 84 | 85 | **Parameters:** 86 | 87 | permission 88 | The permission for which an inheritance block should be added. 89 | Either a permission object or the codename of a permission. 90 | """ 91 | return permissions.utils.add_inheritance_block(self, permission) 92 | 93 | def remove_inheritance_block(self, permission): 94 | """Removes a inheritance block for the passed permission. 95 | 96 | **Parameters:** 97 | 98 | permission 99 | The permission for which an inheritance block should be removed. 100 | Either a permission object or the codename of a permission. 101 | """ 102 | return permissions.utils.remove_inheritance_block(self, permission) 103 | 104 | def is_inherited(self, codename): 105 | """Returns True if the passed permission is inherited. 106 | 107 | **Parameters:** 108 | 109 | codename 110 | The permission which should be checked. Must be the codename of 111 | the permission. 112 | """ 113 | return permissions.utils.is_inherited(self, codename) 114 | 115 | def add_role(self, principal, role): 116 | """Adds a local role for the principal. 117 | 118 | **Parameters:** 119 | 120 | principal 121 | The principal (user or group) which gets the role. 122 | 123 | role 124 | The role which is assigned. 125 | """ 126 | return permissions.utils.add_local_role(self, principal, role) 127 | 128 | def get_roles(self, principal): 129 | """Returns *direct* local roles for passed principal (user or group). 130 | """ 131 | return permissions.utils.get_local_roles(self, principal) 132 | 133 | def remove_role(self, principal, role): 134 | """Removes a local role for the principal to the object. 135 | 136 | **Parameters:** 137 | 138 | principal 139 | The principal (user or group) from which the role is removed. 140 | 141 | role 142 | The role which is removed. 143 | """ 144 | return permissions.utils.remove_local_role(self, principal, role) 145 | 146 | def remove_roles(self, principal): 147 | """Removes all local roles for the passed principal from the object. 148 | 149 | **Parameters:** 150 | 151 | principal 152 | The principal (user or group) from which all local roles are 153 | removed. 154 | """ 155 | return permissions.utils.remove_local_roles(self, principal) 156 | -------------------------------------------------------------------------------- /permissions/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from permissions.models import ObjectPermission 4 | admin.site.register(ObjectPermission) 5 | 6 | from permissions.models import Permission 7 | admin.site.register(Permission) 8 | 9 | from permissions.models import Role 10 | admin.site.register(Role) 11 | 12 | from permissions.models import PrincipalRoleRelation 13 | admin.site.register(PrincipalRoleRelation) 14 | -------------------------------------------------------------------------------- /permissions/backend.py: -------------------------------------------------------------------------------- 1 | # permissions imports 2 | import permissions.utils 3 | 4 | class ObjectPermissionsBackend(object): 5 | """Django backend for object permissions. Needs Django 1.2. 6 | 7 | Use it together with the default ModelBackend like so:: 8 | 9 | AUTHENTICATION_BACKENDS = ( 10 | 'django.contrib.auth.backends.ModelBackend', 11 | 'permissions.backend.ObjectPermissionsBackend', 12 | ) 13 | 14 | Then you can use it like: 15 | 16 | user.has_perm("view", your_object) 17 | 18 | """ 19 | supports_object_permissions = True 20 | supports_anonymous_user = True 21 | 22 | def authenticate(self, username, password): 23 | return None 24 | 25 | def has_permission(self, user_obj, perm, obj=None): 26 | import warnings 27 | warnings.warn( 28 | "The use of has_permission is deprecated, please use the has_perm instead.", 29 | PendingDeprecationWarning 30 | ) 31 | return self.has_perm(user_obj, perm, obj) 32 | 33 | def has_perm(self, user_obj, perm, obj=None): 34 | """Checks whether the passed user has passed permission for passed 35 | object (obj). 36 | 37 | This should be the primary method to check wether a user has a certain 38 | permission. 39 | 40 | Parameters 41 | ========== 42 | 43 | perm 44 | The permission's codename which should be checked. 45 | 46 | user_obj 47 | The user for which the permission should be checked. 48 | 49 | obj 50 | The object for which the permission should be checked. 51 | """ 52 | return permissions.utils.has_permission(obj, user_obj, perm) -------------------------------------------------------------------------------- /permissions/exceptions.py: -------------------------------------------------------------------------------- 1 | class Unauthorized(Exception): 2 | def __init__(self, message): 3 | self.message = message 4 | 5 | def __str__(self): 6 | return repr(self.message) 7 | -------------------------------------------------------------------------------- /permissions/fixtures/initial.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | View 5 | view 6 | 7 | 8 | Edit 9 | edit 10 | 11 | 12 | Delete 13 | delete 14 | 15 | 16 | Cut 17 | cut 18 | 19 | 20 | Copy 21 | copy 22 | 23 | 24 | -------------------------------------------------------------------------------- /permissions/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diefenbach/django-permissions/049cb8227c38753b609415ba2903ab895a20e10e/permissions/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /permissions/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # German translations for django-permissions 2 | # Copyright (C) 2010 Kai Diefenbach 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Kai Diefenbach , 2010. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 1.0\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2010-03-30 23:12+0200\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: models.py:154 19 | msgid "Name" 20 | msgstr "Name" 21 | 22 | #: models.py:155 23 | msgid "Codename" 24 | msgstr "Codename" 25 | 26 | #: models.py:156 27 | msgid "Content Types" 28 | msgstr "Inhaltstypen" 29 | 30 | #: models.py:175 models.py:280 31 | msgid "Role" 32 | msgstr "Rolle" 33 | 34 | #: models.py:176 models.py:216 35 | msgid "Permission" 36 | msgstr "Recht" 37 | 38 | #: models.py:178 models.py:218 models.py:282 39 | msgid "Content type" 40 | msgstr "Inhaltstyp" 41 | 42 | #: models.py:179 models.py:219 models.py:283 43 | msgid "Content id" 44 | msgstr "Inhalts-ID" 45 | 46 | #: models.py:278 47 | msgid "User" 48 | msgstr "Benutzer" 49 | 50 | #: models.py:279 51 | msgid "Group" 52 | msgstr "Gruppe" 53 | -------------------------------------------------------------------------------- /permissions/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('contenttypes', '0002_remove_content_type_name'), 12 | ('auth', '0006_require_contenttypes_0002'), 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='ObjectPermission', 19 | fields=[ 20 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 21 | ('content_id', models.PositiveIntegerField(verbose_name='Content id')), 22 | ('content_type', models.ForeignKey(verbose_name='Content type', to='contenttypes.ContentType')), 23 | ], 24 | ), 25 | migrations.CreateModel( 26 | name='ObjectPermissionInheritanceBlock', 27 | fields=[ 28 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 29 | ('content_id', models.PositiveIntegerField(verbose_name='Content id')), 30 | ('content_type', models.ForeignKey(verbose_name='Content type', to='contenttypes.ContentType')), 31 | ], 32 | ), 33 | migrations.CreateModel( 34 | name='Permission', 35 | fields=[ 36 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 37 | ('name', models.CharField(unique=True, max_length=100, verbose_name='Name')), 38 | ('codename', models.CharField(unique=True, max_length=100, verbose_name='Codename')), 39 | ('content_types', models.ManyToManyField(related_name='content_types', verbose_name='Content Types', to='contenttypes.ContentType', blank=True)), 40 | ], 41 | ), 42 | migrations.CreateModel( 43 | name='PrincipalRoleRelation', 44 | fields=[ 45 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 46 | ('content_id', models.PositiveIntegerField(null=True, verbose_name='Content id', blank=True)), 47 | ('content_type', models.ForeignKey(verbose_name='Content type', blank=True, to='contenttypes.ContentType', null=True)), 48 | ('group', models.ForeignKey(verbose_name='Group', blank=True, to='auth.Group', null=True)), 49 | ], 50 | ), 51 | migrations.CreateModel( 52 | name='Role', 53 | fields=[ 54 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 55 | ('name', models.CharField(unique=True, max_length=100)), 56 | ], 57 | options={ 58 | 'ordering': ('name',), 59 | }, 60 | ), 61 | migrations.AddField( 62 | model_name='principalrolerelation', 63 | name='role', 64 | field=models.ForeignKey(verbose_name='Role', to='permissions.Role'), 65 | ), 66 | migrations.AddField( 67 | model_name='principalrolerelation', 68 | name='user', 69 | field=models.ForeignKey(verbose_name='User', blank=True, to=settings.AUTH_USER_MODEL, null=True), 70 | ), 71 | migrations.AddField( 72 | model_name='objectpermissioninheritanceblock', 73 | name='permission', 74 | field=models.ForeignKey(verbose_name='Permission', to='permissions.Permission'), 75 | ), 76 | migrations.AddField( 77 | model_name='objectpermission', 78 | name='permission', 79 | field=models.ForeignKey(verbose_name='Permission', to='permissions.Permission'), 80 | ), 81 | migrations.AddField( 82 | model_name='objectpermission', 83 | name='role', 84 | field=models.ForeignKey(verbose_name='Role', blank=True, to='permissions.Role', null=True), 85 | ), 86 | ] 87 | -------------------------------------------------------------------------------- /permissions/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diefenbach/django-permissions/049cb8227c38753b609415ba2903ab895a20e10e/permissions/migrations/__init__.py -------------------------------------------------------------------------------- /permissions/models.py: -------------------------------------------------------------------------------- 1 | # django imports 2 | from django.db import models 3 | from django.contrib.auth.models import User 4 | from django.contrib.auth.models import Group 5 | from django.contrib.contenttypes.fields import GenericForeignKey 6 | from django.contrib.contenttypes.models import ContentType 7 | from django.utils.translation import ugettext_lazy as _ 8 | 9 | # permissions imports 10 | import permissions.utils 11 | 12 | 13 | class Permission(models.Model): 14 | """A permission which can be granted to users/groups and objects. 15 | 16 | **Attributes:** 17 | 18 | name 19 | The unique name of the permission. This is displayed to users. 20 | 21 | codename 22 | The unique codename of the permission. This is used internal to 23 | identify a permission. 24 | 25 | content_types 26 | The content types for which the permission is active. This can be 27 | used to display only reasonable permissions for an object. 28 | """ 29 | name = models.CharField(_(u"Name"), max_length=100, unique=True) 30 | codename = models.CharField(_(u"Codename"), max_length=100, unique=True) 31 | content_types = models.ManyToManyField(ContentType, verbose_name=_(u"Content Types"), blank=True, related_name="content_types") 32 | 33 | class Meta: 34 | app_label = "permissions" 35 | 36 | def __unicode__(self): 37 | return "%s (%s)" % (self.name, self.codename) 38 | 39 | 40 | class ObjectPermission(models.Model): 41 | """Grants permission for a role and an content object (optional). 42 | 43 | **Attributes:** 44 | 45 | role 46 | The role for which the permission is granted. 47 | 48 | permission 49 | The permission which is granted. 50 | 51 | content 52 | The object for which the permission is granted. 53 | """ 54 | role = models.ForeignKey("Role", verbose_name=_(u"Role"), blank=True, null=True) 55 | permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) 56 | content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type")) 57 | content_id = models.PositiveIntegerField(verbose_name=_(u"Content id")) 58 | content = GenericForeignKey(ct_field="content_type", fk_field="content_id") 59 | 60 | class Meta: 61 | app_label = "permissions" 62 | 63 | def __unicode__(self): 64 | return "%s / %s / %s - %s" % (self.permission.name, self.role, self.content_type, self.content_id) 65 | 66 | 67 | class ObjectPermissionInheritanceBlock(models.Model): 68 | """Blocks the inheritance for specific permission and object. 69 | 70 | **Attributes:** 71 | 72 | permission 73 | The permission for which inheritance is blocked. 74 | 75 | content 76 | The object for which the inheritance is blocked. 77 | """ 78 | permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) 79 | content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type")) 80 | content_id = models.PositiveIntegerField(verbose_name=_(u"Content id")) 81 | content = GenericForeignKey(ct_field="content_type", fk_field="content_id") 82 | 83 | class Meta: 84 | app_label = "permissions" 85 | 86 | def __unicode__(self): 87 | return "%s / %s - %s" % (self.permission, self.content_type, self.content_id) 88 | 89 | 90 | class Role(models.Model): 91 | """A role gets permissions to do something. Principals (users and groups) 92 | can only get permissions via roles. 93 | 94 | **Attributes:** 95 | 96 | name 97 | The unique name of the role 98 | """ 99 | name = models.CharField(max_length=100, unique=True) 100 | 101 | class Meta: 102 | app_label = "permissions" 103 | ordering = ("name", ) 104 | 105 | def __unicode__(self): 106 | return self.name 107 | 108 | def add_principal(self, principal, content=None): 109 | """Addes the given principal (user or group) ot the Role. 110 | """ 111 | return permissions.utils.add_role(principal, self) 112 | 113 | def get_groups(self, content=None): 114 | """Returns all groups which has this role assigned. If content is given 115 | it returns also the local roles. 116 | """ 117 | if content: 118 | ctype = ContentType.objects.get_for_model(content) 119 | prrs = PrincipalRoleRelation.objects.filter(role=self, 120 | content_id__in=(None, content.id), 121 | content_type__in=(None, ctype)).exclude(group=None) 122 | else: 123 | prrs = PrincipalRoleRelation.objects.filter(role=self, 124 | content_id=None, content_type=None).exclude(group=None) 125 | 126 | return [prr.group for prr in prrs] 127 | 128 | def get_users(self, content=None): 129 | """Returns all users which has this role assigned. If content is given 130 | it returns also the local roles. 131 | """ 132 | if content: 133 | ctype = ContentType.objects.get_for_model(content) 134 | prrs = PrincipalRoleRelation.objects.filter(role=self, 135 | content_id__in=(None, content.id), 136 | content_type__in=(None, ctype)).exclude(user=None) 137 | else: 138 | prrs = PrincipalRoleRelation.objects.filter(role=self, 139 | content_id=None, content_type=None).exclude(user=None) 140 | 141 | return [prr.user for prr in prrs] 142 | 143 | 144 | class PrincipalRoleRelation(models.Model): 145 | """A role given to a principal (user or group). If a content object is 146 | given this is a local role, i.e. the principal has this role only for this 147 | content object. Otherwise it is a global role, i.e. the principal has 148 | this role generally. 149 | 150 | user 151 | A user instance. Either a user xor a group needs to be given. 152 | 153 | group 154 | A group instance. Either a user xor a group needs to be given. 155 | 156 | role 157 | The role which is given to the principal for content. 158 | 159 | content 160 | The content object which gets the local role (optional). 161 | """ 162 | user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True) 163 | group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True) 164 | role = models.ForeignKey(Role, verbose_name=_(u"Role")) 165 | content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), blank=True, null=True) 166 | content_id = models.PositiveIntegerField(verbose_name=_(u"Content id"), blank=True, null=True) 167 | content = GenericForeignKey(ct_field="content_type", fk_field="content_id") 168 | 169 | class Meta: 170 | app_label = "permissions" 171 | 172 | def __unicode__(self): 173 | if self.user: 174 | principal = self.user.username 175 | else: 176 | principal = self.group 177 | 178 | return "%s - %s" % (principal, self.role) 179 | 180 | def get_principal(self): 181 | """Returns the principal. 182 | """ 183 | return self.user or self.group 184 | 185 | def set_principal(self, principal): 186 | """Sets the principal. 187 | """ 188 | if isinstance(principal, User): 189 | self.user = principal 190 | else: 191 | self.group = principal 192 | 193 | principal = property(get_principal, set_principal) 194 | -------------------------------------------------------------------------------- /permissions/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diefenbach/django-permissions/049cb8227c38753b609415ba2903ab895a20e10e/permissions/templatetags/__init__.py -------------------------------------------------------------------------------- /permissions/templatetags/permissions_tags.py: -------------------------------------------------------------------------------- 1 | # django imports 2 | from django import template 3 | 4 | import permissions.utils 5 | register = template.Library() 6 | 7 | class PermissionComparisonNode(template.Node): 8 | """Implements a node to provide an if current user has passed permission 9 | for current object. 10 | """ 11 | @classmethod 12 | def handle_token(cls, parser, token): 13 | bits = token.contents.split() 14 | if len(bits) != 2: 15 | raise template.TemplateSyntaxError( 16 | "'%s' tag takes one argument" % bits[0]) 17 | end_tag = 'endifhasperm' 18 | nodelist_true = parser.parse(('else', end_tag)) 19 | token = parser.next_token() 20 | if token.contents == 'else': # there is an 'else' clause in the tag 21 | nodelist_false = parser.parse((end_tag,)) 22 | parser.delete_first_token() 23 | else: 24 | nodelist_false = "" 25 | 26 | return cls(bits[1], nodelist_true, nodelist_false) 27 | 28 | def __init__(self, codename, nodelist_true, nodelist_false): 29 | self.codename = codename 30 | self.nodelist_true = nodelist_true 31 | self.nodelist_false = nodelist_false 32 | 33 | def render(self, context): 34 | obj = context.get("obj") 35 | request = context.get("request") 36 | if permissions.utils.has_permission(obj, request.user, self.codename): 37 | return self.nodelist_true.render(context) 38 | else: 39 | return self.nodelist_false 40 | 41 | @register.tag 42 | def ifhasperm(parser, token): 43 | """This function provides functionality for the 'ifhasperm' template tag. 44 | """ 45 | return PermissionComparisonNode.handle_token(parser, token) 46 | 47 | -------------------------------------------------------------------------------- /permissions/tests.py: -------------------------------------------------------------------------------- 1 | # django imports 2 | from django.contrib.flatpages.models import FlatPage 3 | from django.contrib.auth.models import Group 4 | from django.contrib.auth.models import User 5 | from django.conf import settings 6 | from django.core.urlresolvers import reverse 7 | from django.test import TestCase 8 | from django.test.client import Client 9 | 10 | # permissions imports 11 | from permissions.models import Permission 12 | from permissions.models import ObjectPermission 13 | from permissions.models import ObjectPermissionInheritanceBlock 14 | from permissions.models import Role 15 | 16 | import permissions.utils 17 | 18 | class BackendTestCase(TestCase): 19 | """ 20 | """ 21 | def setUp(self): 22 | """ 23 | """ 24 | settings.AUTHENTICATION_BACKENDS = ( 25 | 'django.contrib.auth.backends.ModelBackend', 26 | 'permissions.backend.ObjectPermissionsBackend', 27 | ) 28 | 29 | self.role_1 = permissions.utils.register_role("Role 1") 30 | self.user = User.objects.create(username="john") 31 | self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") 32 | self.view = permissions.utils.register_permission("View", "view") 33 | 34 | # Add user to role 35 | self.role_1.add_principal(self.user) 36 | 37 | def test_has_perm(self): 38 | """Tests has perm of the backend. 39 | """ 40 | result = self.user.has_perm(self.view, self.page_1) 41 | self.assertEqual(result, False) 42 | 43 | # assign view permission to role 1 44 | permissions.utils.grant_permission(self.page_1, self.role_1, self.view) 45 | 46 | result = self.user.has_perm("view", self.page_1) 47 | self.assertEqual(result, True) 48 | 49 | class RoleTestCase(TestCase): 50 | """ 51 | """ 52 | def setUp(self): 53 | """ 54 | """ 55 | self.role_1 = permissions.utils.register_role("Role 1") 56 | self.role_2 = permissions.utils.register_role("Role 2") 57 | 58 | self.user = User.objects.create(username="john") 59 | self.group = Group.objects.create(name="brights") 60 | 61 | self.user.groups.add(self.group) 62 | 63 | self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") 64 | self.page_2 = FlatPage.objects.create(url="/page-1/", title="Page 2") 65 | 66 | def test_getter(self): 67 | """ 68 | """ 69 | # Group 70 | result = permissions.utils.get_group(self.group.id) 71 | self.assertEqual(result, self.group) 72 | 73 | result = permissions.utils.get_group(42) 74 | self.assertEqual(result, None) 75 | 76 | result = permissions.utils.get_group(self.group.name) 77 | self.assertEqual(result, self.group) 78 | 79 | result = permissions.utils.get_group("Not Existing") 80 | self.assertEqual(result, None) 81 | 82 | # Role 83 | result = permissions.utils.get_role(self.role_1.id) 84 | self.assertEqual(result, self.role_1) 85 | 86 | result = permissions.utils.get_role(42) 87 | self.assertEqual(result, None) 88 | 89 | result = permissions.utils.get_role(self.role_1.name) 90 | self.assertEqual(result, self.role_1) 91 | 92 | result = permissions.utils.get_role("Not Existing") 93 | self.assertEqual(result, None) 94 | 95 | result = permissions.utils.get_user(self.user.id) 96 | self.assertEqual(result, self.user) 97 | 98 | result = permissions.utils.get_user(42) 99 | self.assertEqual(result, None) 100 | 101 | result = permissions.utils.get_user(self.user.username) 102 | self.assertEqual(result, self.user) 103 | 104 | result = permissions.utils.get_user("Not Existing") 105 | self.assertEqual(result, None) 106 | 107 | def test_global_roles_user(self): 108 | """ 109 | """ 110 | # Add role 1 111 | result = permissions.utils.add_role(self.user, self.role_1) 112 | self.assertEqual(result, True) 113 | 114 | # Add role 1 again 115 | result = permissions.utils.add_role(self.user, self.role_1) 116 | self.assertEqual(result, False) 117 | 118 | result = permissions.utils.get_roles(self.user) 119 | self.assertEqual(list(result), [self.role_1]) 120 | 121 | # Add role 2 122 | result = permissions.utils.add_role(self.user, self.role_2) 123 | self.assertEqual(result, True) 124 | 125 | delattr(self.user, "roles") 126 | result = permissions.utils.get_roles(self.user) 127 | self.assertEqual(list(result), [self.role_1, self.role_2]) 128 | 129 | # Remove role 1 130 | result = permissions.utils.remove_role(self.user, self.role_1) 131 | self.assertEqual(result, True) 132 | 133 | # Remove role 1 again 134 | result = permissions.utils.remove_role(self.user, self.role_1) 135 | self.assertEqual(result, False) 136 | 137 | delattr(self.user, "roles") 138 | result = permissions.utils.get_roles(self.user) 139 | self.assertEqual(list(result), [self.role_2]) 140 | 141 | # Remove role 2 142 | result = permissions.utils.remove_role(self.user, self.role_2) 143 | self.assertEqual(result, True) 144 | 145 | delattr(self.user, "roles") 146 | result = permissions.utils.get_roles(self.user) 147 | self.assertEqual(list(result), []) 148 | 149 | def test_global_roles_group(self): 150 | """ 151 | """ 152 | # Add role 1 153 | result = permissions.utils.add_role(self.group, self.role_1) 154 | self.assertEqual(result, True) 155 | 156 | # Add role 1 again 157 | result = permissions.utils.add_role(self.group, self.role_1) 158 | self.assertEqual(result, False) 159 | 160 | result = permissions.utils.get_global_roles(self.group) 161 | self.assertEqual(result, [self.role_1]) 162 | 163 | # Add role 2 164 | result = permissions.utils.add_role(self.group, self.role_2) 165 | self.assertEqual(result, True) 166 | 167 | result = permissions.utils.get_global_roles(self.group) 168 | self.assertEqual(result, [self.role_1, self.role_2]) 169 | 170 | # Remove role 1 171 | result = permissions.utils.remove_role(self.group, self.role_1) 172 | self.assertEqual(result, True) 173 | 174 | # Remove role 1 again 175 | result = permissions.utils.remove_role(self.group, self.role_1) 176 | self.assertEqual(result, False) 177 | 178 | result = permissions.utils.get_global_roles(self.group) 179 | self.assertEqual(result, [self.role_2]) 180 | 181 | # Remove role 2 182 | result = permissions.utils.remove_role(self.group, self.role_2) 183 | self.assertEqual(result, True) 184 | 185 | result = permissions.utils.get_global_roles(self.group) 186 | self.assertEqual(result, []) 187 | 188 | def test_remove_roles_user(self): 189 | """ 190 | """ 191 | # Add role 1 192 | result = permissions.utils.add_role(self.user, self.role_1) 193 | self.assertEqual(result, True) 194 | 195 | # Add role 2 196 | result = permissions.utils.add_role(self.user, self.role_2) 197 | self.assertEqual(result, True) 198 | 199 | result = permissions.utils.get_roles(self.user) 200 | self.assertEqual(list(result), [self.role_1, self.role_2]) 201 | 202 | # Remove roles 203 | result = permissions.utils.remove_roles(self.user) 204 | self.assertEqual(result, True) 205 | 206 | delattr(self.user, "roles") 207 | result = permissions.utils.get_roles(self.user) 208 | self.assertEqual(list(result), []) 209 | 210 | # Remove roles 211 | result = permissions.utils.remove_roles(self.user) 212 | self.assertEqual(result, False) 213 | 214 | def test_remove_roles_group(self): 215 | """ 216 | """ 217 | # Add role 1 218 | result = permissions.utils.add_role(self.group, self.role_1) 219 | self.assertEqual(result, True) 220 | 221 | # Add role 2 222 | result = permissions.utils.add_role(self.group, self.role_2) 223 | self.assertEqual(result, True) 224 | 225 | result = permissions.utils.get_global_roles(self.group) 226 | self.assertEqual(result, [self.role_1, self.role_2]) 227 | 228 | # Remove roles 229 | result = permissions.utils.remove_roles(self.group) 230 | self.assertEqual(result, True) 231 | 232 | result = permissions.utils.get_global_roles(self.group) 233 | self.assertEqual(result, []) 234 | 235 | # Remove roles 236 | result = permissions.utils.remove_roles(self.group) 237 | self.assertEqual(result, False) 238 | 239 | def test_local_role_user(self): 240 | """ 241 | """ 242 | # Add local role to page 1 243 | result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) 244 | self.assertEqual(result, True) 245 | 246 | # Again 247 | result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) 248 | self.assertEqual(result, False) 249 | 250 | result = permissions.utils.get_local_roles(self.page_1, self.user) 251 | self.assertEqual(result, [self.role_1]) 252 | 253 | # Add local role 2 254 | result = permissions.utils.add_local_role(self.page_1, self.user, self.role_2) 255 | self.assertEqual(result, True) 256 | 257 | result = permissions.utils.get_local_roles(self.page_1, self.user) 258 | self.assertEqual(result, [self.role_1, self.role_2]) 259 | 260 | # Remove role 1 261 | result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_1) 262 | self.assertEqual(result, True) 263 | 264 | # Remove role 1 again 265 | result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_1) 266 | self.assertEqual(result, False) 267 | 268 | result = permissions.utils.get_local_roles(self.page_1, self.user) 269 | self.assertEqual(result, [self.role_2]) 270 | 271 | # Remove role 2 272 | result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_2) 273 | self.assertEqual(result, True) 274 | 275 | result = permissions.utils.get_local_roles(self.page_1, self.user) 276 | self.assertEqual(result, []) 277 | 278 | def test_local_role_group(self): 279 | """ 280 | """ 281 | # Add local role to page 1 282 | result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) 283 | self.assertEqual(result, True) 284 | 285 | # Again 286 | result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) 287 | self.assertEqual(result, False) 288 | 289 | result = permissions.utils.get_local_roles(self.page_1, self.group) 290 | self.assertEqual(result, [self.role_1]) 291 | 292 | # Add local role 2 293 | result = permissions.utils.add_local_role(self.page_1, self.group, self.role_2) 294 | self.assertEqual(result, True) 295 | 296 | result = permissions.utils.get_local_roles(self.page_1, self.group) 297 | self.assertEqual(result, [self.role_1, self.role_2]) 298 | 299 | # Remove role 1 300 | result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_1) 301 | self.assertEqual(result, True) 302 | 303 | # Remove role 1 again 304 | result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_1) 305 | self.assertEqual(result, False) 306 | 307 | result = permissions.utils.get_local_roles(self.page_1, self.group) 308 | self.assertEqual(result, [self.role_2]) 309 | 310 | # Remove role 2 311 | result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_2) 312 | self.assertEqual(result, True) 313 | 314 | result = permissions.utils.get_local_roles(self.page_1, self.group) 315 | self.assertEqual(result, []) 316 | 317 | def test_remove_local_roles_user(self): 318 | """ 319 | """ 320 | # Add local role to page 1 321 | result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) 322 | self.assertEqual(result, True) 323 | 324 | # Add local role 2 325 | result = permissions.utils.add_local_role(self.page_1, self.user, self.role_2) 326 | self.assertEqual(result, True) 327 | 328 | result = permissions.utils.get_local_roles(self.page_1, self.user) 329 | self.assertEqual(result, [self.role_1, self.role_2]) 330 | 331 | # Remove all local roles 332 | result = permissions.utils.remove_local_roles(self.page_1, self.user) 333 | self.assertEqual(result, True) 334 | 335 | result = permissions.utils.get_local_roles(self.page_1, self.user) 336 | self.assertEqual(result, []) 337 | 338 | # Remove all local roles again 339 | result = permissions.utils.remove_local_roles(self.page_1, self.user) 340 | self.assertEqual(result, False) 341 | 342 | def test_get_groups_1(self): 343 | """Tests global roles for groups. 344 | """ 345 | result = self.role_1.get_groups() 346 | self.assertEqual(len(result), 0) 347 | 348 | result = permissions.utils.add_role(self.group, self.role_1) 349 | self.assertEqual(result, True) 350 | 351 | result = self.role_1.get_groups() 352 | self.assertEqual(result[0].name, "brights") 353 | 354 | # Add another group 355 | self.group_2 = Group.objects.create(name="atheists") 356 | result = permissions.utils.add_role(self.group_2, self.role_1) 357 | 358 | result = self.role_1.get_groups() 359 | self.assertEqual(result[0].name, "brights") 360 | self.assertEqual(result[1].name, "atheists") 361 | self.assertEqual(len(result), 2) 362 | 363 | # Add the role to an user 364 | result = permissions.utils.add_role(self.user, self.role_1) 365 | self.assertEqual(result, True) 366 | 367 | # This shouldn't have an effect on the result 368 | result = self.role_1.get_groups() 369 | self.assertEqual(result[0].name, "brights") 370 | self.assertEqual(result[1].name, "atheists") 371 | self.assertEqual(len(result), 2) 372 | 373 | def test_get_groups_2(self): 374 | """Tests local roles for groups. 375 | """ 376 | result = self.role_1.get_groups(self.page_1) 377 | self.assertEqual(len(result), 0) 378 | 379 | result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) 380 | self.assertEqual(result, True) 381 | 382 | result = self.role_1.get_groups(self.page_1) 383 | self.assertEqual(result[0].name, "brights") 384 | 385 | # Add another local group 386 | self.group_2 = Group.objects.create(name="atheists") 387 | result = permissions.utils.add_local_role(self.page_1, self.group_2, self.role_1) 388 | 389 | result = self.role_1.get_groups(self.page_1) 390 | self.assertEqual(result[0].name, "brights") 391 | self.assertEqual(result[1].name, "atheists") 392 | 393 | # A the global role to group 394 | result = permissions.utils.add_role(self.group, self.role_1) 395 | self.assertEqual(result, True) 396 | 397 | # Nontheless there are just two groups returned (and no duplicate) 398 | result = self.role_1.get_groups(self.page_1) 399 | self.assertEqual(result[0].name, "brights") 400 | self.assertEqual(result[1].name, "atheists") 401 | self.assertEqual(len(result), 2) 402 | 403 | # Andere there should one global role 404 | result = self.role_1.get_groups() 405 | self.assertEqual(result[0].name, "brights") 406 | 407 | # Add the role to an user 408 | result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) 409 | self.assertEqual(result, True) 410 | 411 | # This shouldn't have an effect on the result 412 | result = self.role_1.get_groups(self.page_1) 413 | self.assertEqual(result[0].name, "brights") 414 | self.assertEqual(result[1].name, "atheists") 415 | self.assertEqual(len(result), 2) 416 | 417 | def test_get_users_1(self): 418 | """Tests global roles for users. 419 | """ 420 | result = self.role_1.get_users() 421 | self.assertEqual(len(result), 0) 422 | 423 | result = permissions.utils.add_role(self.user, self.role_1) 424 | self.assertEqual(result, True) 425 | 426 | result = self.role_1.get_users() 427 | self.assertEqual(result[0].username, "john") 428 | 429 | # Add another role to an user 430 | self.user_2 = User.objects.create(username="jane") 431 | result = permissions.utils.add_role(self.user_2, self.role_1) 432 | 433 | result = self.role_1.get_users() 434 | self.assertEqual(result[0].username, "john") 435 | self.assertEqual(result[1].username, "jane") 436 | self.assertEqual(len(result), 2) 437 | 438 | # Add the role to an user 439 | result = permissions.utils.add_role(self.group, self.role_1) 440 | self.assertEqual(result, True) 441 | 442 | # This shouldn't have an effect on the result 443 | result = self.role_1.get_users() 444 | self.assertEqual(result[0].username, "john") 445 | self.assertEqual(result[1].username, "jane") 446 | self.assertEqual(len(result), 2) 447 | 448 | def test_get_users_2(self): 449 | """Tests local roles for users. 450 | """ 451 | result = self.role_1.get_users(self.page_1) 452 | self.assertEqual(len(result), 0) 453 | 454 | result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) 455 | self.assertEqual(result, True) 456 | 457 | result = self.role_1.get_users(self.page_1) 458 | self.assertEqual(result[0].username, "john") 459 | 460 | # Add another local role to an user 461 | self.user_2 = User.objects.create(username="jane") 462 | result = permissions.utils.add_local_role(self.page_1, self.user_2, self.role_1) 463 | 464 | result = self.role_1.get_users(self.page_1) 465 | self.assertEqual(result[0].username, "john") 466 | self.assertEqual(result[1].username, "jane") 467 | 468 | # A the global role to user 469 | result = permissions.utils.add_role(self.user, self.role_1) 470 | self.assertEqual(result, True) 471 | 472 | # Nontheless there are just two users returned (and no duplicate) 473 | result = self.role_1.get_users(self.page_1) 474 | self.assertEqual(result[0].username, "john") 475 | self.assertEqual(result[1].username, "jane") 476 | self.assertEqual(len(result), 2) 477 | 478 | # Andere there should one user for the global role 479 | result = self.role_1.get_users() 480 | self.assertEqual(result[0].username, "john") 481 | 482 | # Add the role to an group 483 | result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) 484 | self.assertEqual(result, True) 485 | 486 | # This shouldn't have an effect on the result 487 | result = self.role_1.get_users(self.page_1) 488 | self.assertEqual(result[0].username, "john") 489 | self.assertEqual(result[1].username, "jane") 490 | self.assertEqual(len(result), 2) 491 | 492 | def test_get_users_3(self): 493 | """ 494 | """ 495 | self.role_1.add_principal(self.user) 496 | result = self.role_1.get_users() 497 | self.assertEqual(result, [self.user]) 498 | 499 | # Add same user again 500 | self.role_1.add_principal(self.user) 501 | result = self.role_1.get_users() 502 | self.assertEqual(result, [self.user]) 503 | 504 | class PermissionTestCase(TestCase): 505 | """ 506 | """ 507 | def setUp(self): 508 | """ 509 | """ 510 | self.role_1 = permissions.utils.register_role("Role 1") 511 | self.role_2 = permissions.utils.register_role("Role 2") 512 | 513 | self.user = User.objects.create(username="john") 514 | permissions.utils.add_role(self.user, self.role_1) 515 | self.user.save() 516 | 517 | self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") 518 | self.page_2 = FlatPage.objects.create(url="/page-2/", title="Page 2") 519 | 520 | self.permission = permissions.utils.register_permission("View", "view") 521 | 522 | def test_add_permissions(self): 523 | """ 524 | """ 525 | # Add per object 526 | result = permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) 527 | self.assertEqual(result, True) 528 | 529 | # Add per codename 530 | result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") 531 | self.assertEqual(result, True) 532 | 533 | # Add ermission which does not exist 534 | result = permissions.utils.grant_permission(self.page_1, self.role_1, "hurz") 535 | self.assertEqual(result, False) 536 | 537 | def test_remove_permission(self): 538 | """ 539 | """ 540 | # Add 541 | result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") 542 | self.assertEqual(result, True) 543 | 544 | # Remove 545 | result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") 546 | self.assertEqual(result, True) 547 | 548 | # Remove again 549 | result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") 550 | self.assertEqual(result, False) 551 | 552 | def test_has_permission_role(self): 553 | """ 554 | """ 555 | result = permissions.utils.has_permission(self.page_1, self.user, "view") 556 | self.assertEqual(result, False) 557 | 558 | result = permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) 559 | self.assertEqual(result, True) 560 | 561 | result = permissions.utils.has_permission(self.page_1, self.user, "view") 562 | self.assertEqual(result, True) 563 | 564 | result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") 565 | self.assertEqual(result, True) 566 | 567 | result = permissions.utils.has_permission(self.page_1, self.user, "view") 568 | self.assertEqual(result, False) 569 | 570 | def test_has_permission_owner(self): 571 | """ 572 | """ 573 | creator = User.objects.create(username="jane") 574 | 575 | result = permissions.utils.has_permission(self.page_1, creator, "view") 576 | self.assertEqual(result, False) 577 | 578 | owner = permissions.utils.register_role("Owner") 579 | permissions.utils.grant_permission(self.page_1, owner, "view") 580 | 581 | result = permissions.utils.has_permission(self.page_1, creator, "view", [owner]) 582 | self.assertEqual(result, True) 583 | 584 | def test_local_role(self): 585 | """ 586 | """ 587 | result = permissions.utils.has_permission(self.page_1, self.user, "view") 588 | self.assertEqual(result, False) 589 | 590 | permissions.utils.grant_permission(self.page_1, self.role_2, self.permission) 591 | permissions.utils.add_local_role(self.page_1, self.user, self.role_2) 592 | # Remove cache. This is done automatically by Django for every new request 593 | delattr(self.user, "roles") 594 | 595 | result = permissions.utils.has_permission(self.page_1, self.user, "view") 596 | self.assertEqual(result, True) 597 | 598 | def test_ineritance(self): 599 | """ 600 | """ 601 | result = permissions.utils.is_inherited(self.page_1, "view") 602 | self.assertEqual(result, True) 603 | 604 | # per permission 605 | permissions.utils.add_inheritance_block(self.page_1, self.permission) 606 | 607 | result = permissions.utils.is_inherited(self.page_1, "view") 608 | self.assertEqual(result, False) 609 | 610 | permissions.utils.remove_inheritance_block(self.page_1, self.permission) 611 | 612 | result = permissions.utils.is_inherited(self.page_1, "view") 613 | self.assertEqual(result, True) 614 | 615 | # per codename 616 | permissions.utils.add_inheritance_block(self.page_1, "view") 617 | 618 | result = permissions.utils.is_inherited(self.page_1, "view") 619 | self.assertEqual(result, False) 620 | 621 | permissions.utils.remove_inheritance_block(self.page_1, "view") 622 | 623 | result = permissions.utils.is_inherited(self.page_1, "view") 624 | self.assertEqual(result, True) 625 | 626 | def test_unicode(self): 627 | """ 628 | """ 629 | # Permission 630 | self.assertEqual(self.permission.__unicode__(), u"View (view)") 631 | 632 | # ObjectPermission 633 | permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) 634 | opr = ObjectPermission.objects.get(permission=self.permission, role=self.role_1) 635 | self.assertEqual(opr.__unicode__(), u"View / %s / flat page - %s" % (self.role_1, self.page_1.id)) 636 | 637 | # ObjectPermissionInheritanceBlock 638 | permissions.utils.add_inheritance_block(self.page_1, self.permission) 639 | opb = ObjectPermissionInheritanceBlock.objects.get(permission=self.permission) 640 | 641 | self.assertEqual(opb.__unicode__(), u"View (view) / flat page - %s" % (self.page_1.id)) 642 | 643 | def test_reset(self): 644 | """ 645 | """ 646 | result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") 647 | self.assertEqual(result, True) 648 | 649 | result = permissions.utils.has_permission(self.page_1, self.user, "view") 650 | self.assertEqual(result, True) 651 | 652 | permissions.utils.add_inheritance_block(self.page_1, "view") 653 | 654 | result = permissions.utils.is_inherited(self.page_1, "view") 655 | self.assertEqual(result, False) 656 | 657 | permissions.utils.reset(self.page_1) 658 | 659 | result = permissions.utils.has_permission(self.page_1, self.user, "view") 660 | self.assertEqual(result, False) 661 | 662 | result = permissions.utils.is_inherited(self.page_1, "view") 663 | self.assertEqual(result, True) 664 | 665 | permissions.utils.reset(self.page_1) 666 | 667 | class RegistrationTestCase(TestCase): 668 | """Tests the registration of different components. 669 | """ 670 | def test_group(self): 671 | """Tests registering/unregistering of a group. 672 | """ 673 | # Register a group 674 | result = permissions.utils.register_group("Brights") 675 | self.failUnless(isinstance(result, Group)) 676 | 677 | # It's there 678 | group = Group.objects.get(name="Brights") 679 | self.assertEqual(group.name, "Brights") 680 | 681 | # Trying to register another group with same name 682 | result = permissions.utils.register_group("Brights") 683 | self.assertEqual(result, False) 684 | 685 | group = Group.objects.get(name="Brights") 686 | self.assertEqual(group.name, "Brights") 687 | 688 | # Unregister the group 689 | result = permissions.utils.unregister_group("Brights") 690 | self.assertEqual(result, True) 691 | 692 | # It's not there anymore 693 | self.assertRaises(Group.DoesNotExist, Group.objects.get, name="Brights") 694 | 695 | # Trying to unregister the group again 696 | result = permissions.utils.unregister_group("Brights") 697 | self.assertEqual(result, False) 698 | 699 | def test_role(self): 700 | """Tests registering/unregistering of a role. 701 | """ 702 | # Register a role 703 | result = permissions.utils.register_role("Editor") 704 | self.failUnless(isinstance(result, Role)) 705 | 706 | # It's there 707 | role = Role.objects.get(name="Editor") 708 | self.assertEqual(role.name, "Editor") 709 | 710 | # Trying to register another role with same name 711 | result = permissions.utils.register_role("Editor") 712 | self.assertEqual(result, False) 713 | 714 | role = Role.objects.get(name="Editor") 715 | self.assertEqual(role.name, "Editor") 716 | 717 | # Unregister the role 718 | result = permissions.utils.unregister_role("Editor") 719 | self.assertEqual(result, True) 720 | 721 | # It's not there anymore 722 | self.assertRaises(Role.DoesNotExist, Role.objects.get, name="Editor") 723 | 724 | # Trying to unregister the role again 725 | result = permissions.utils.unregister_role("Editor") 726 | self.assertEqual(result, False) 727 | 728 | def test_permission(self): 729 | """Tests registering/unregistering of a permission. 730 | """ 731 | # Register a permission 732 | result = permissions.utils.register_permission("Change", "change") 733 | self.failUnless(isinstance(result, Permission)) 734 | 735 | # Is it there? 736 | p = Permission.objects.get(codename="change") 737 | self.assertEqual(p.name, "Change") 738 | 739 | # Register a permission with the same codename 740 | result = permissions.utils.register_permission("Change2", "change") 741 | self.assertEqual(result, False) 742 | 743 | # Is it there? 744 | p = Permission.objects.get(codename="change") 745 | self.assertEqual(p.name, "Change") 746 | 747 | # Register a permission with the same name 748 | result = permissions.utils.register_permission("Change", "change2") 749 | self.assertEqual(result, False) 750 | 751 | # Is it there? 752 | p = Permission.objects.get(codename="change") 753 | self.assertEqual(p.name, "Change") 754 | 755 | # Unregister the permission 756 | result = permissions.utils.unregister_permission("change") 757 | self.assertEqual(result, True) 758 | 759 | # Is it not there anymore? 760 | self.assertRaises(Permission.DoesNotExist, Permission.objects.get, codename="change") 761 | 762 | # Unregister the permission again 763 | result = permissions.utils.unregister_permission("change") 764 | self.assertEqual(result, False) 765 | 766 | # django imports 767 | from django.core.handlers.wsgi import WSGIRequest 768 | from django.contrib.auth.models import User 769 | from django.contrib.sessions.backends.file import SessionStore 770 | from django.test.client import Client 771 | 772 | # Taken from "http://www.djangosnippets.org/snippets/963/" 773 | class RequestFactory(Client): 774 | """ 775 | Class that lets you create mock Request objects for use in testing. 776 | 777 | Usage: 778 | 779 | rf = RequestFactory() 780 | get_request = rf.get('/hello/') 781 | post_request = rf.post('/submit/', {'foo': 'bar'}) 782 | 783 | This class re-uses the django.test.client.Client interface, docs here: 784 | http://www.djangoproject.com/documentation/testing/#the-test-client 785 | 786 | Once you have a request object you can pass it to any view function, 787 | just as if that view had been hooked up using a URLconf. 788 | 789 | """ 790 | def request(self, **request): 791 | """ 792 | Similar to parent class, but returns the request object as soon as it 793 | has created it. 794 | """ 795 | environ = { 796 | 'HTTP_COOKIE': self.cookies, 797 | 'PATH_INFO': '/', 798 | 'QUERY_STRING': '', 799 | 'REQUEST_METHOD': 'GET', 800 | 'SCRIPT_NAME': '', 801 | 'SERVER_NAME': 'testserver', 802 | 'SERVER_PORT': 80, 803 | 'SERVER_PROTOCOL': 'HTTP/1.1', 804 | } 805 | environ.update(self.defaults) 806 | environ.update(request) 807 | return WSGIRequest(environ) 808 | 809 | def create_request(): 810 | """ 811 | """ 812 | rf = RequestFactory() 813 | request = rf.get('/') 814 | request.session = SessionStore() 815 | 816 | user = User() 817 | user.is_superuser = True 818 | user.save() 819 | request.user = user 820 | 821 | return request 822 | -------------------------------------------------------------------------------- /permissions/utils.py: -------------------------------------------------------------------------------- 1 | # python imports 2 | import warnings 3 | 4 | # django imports 5 | from django.db import IntegrityError 6 | from django.db import connection 7 | from django.db.models import Q 8 | from django.contrib.auth.models import User 9 | from django.contrib.auth.models import Group 10 | from django.contrib.contenttypes.models import ContentType 11 | from django.core.exceptions import ObjectDoesNotExist 12 | 13 | # permissions imports 14 | from permissions.exceptions import Unauthorized 15 | from permissions.models import ObjectPermission 16 | from permissions.models import ObjectPermissionInheritanceBlock 17 | from permissions.models import Permission 18 | from permissions.models import PrincipalRoleRelation 19 | from permissions.models import Role 20 | 21 | # Roles ###################################################################### 22 | 23 | def add_role(principal, role): 24 | """Adds a global role to a principal. 25 | 26 | **Parameters:** 27 | 28 | principal 29 | The principal (user or group) which gets the role added. 30 | 31 | role 32 | The role which is assigned. 33 | """ 34 | if isinstance(principal, User): 35 | try: 36 | PrincipalRoleRelation.objects.get(user=principal, role=role, content_id=None, content_type=None) 37 | except PrincipalRoleRelation.DoesNotExist: 38 | PrincipalRoleRelation.objects.create(user=principal, role=role) 39 | return True 40 | else: 41 | try: 42 | PrincipalRoleRelation.objects.get(group=principal, role=role, content_id=None, content_type=None) 43 | except PrincipalRoleRelation.DoesNotExist: 44 | PrincipalRoleRelation.objects.create(group=principal, role=role) 45 | return True 46 | 47 | return False 48 | 49 | def add_local_role(obj, principal, role): 50 | """Adds a local role to a principal. 51 | 52 | **Parameters:** 53 | 54 | obj 55 | The object for which the principal gets the role. 56 | 57 | principal 58 | The principal (user or group) which gets the role. 59 | 60 | role 61 | The role which is assigned. 62 | """ 63 | ctype = ContentType.objects.get_for_model(obj) 64 | if isinstance(principal, User): 65 | try: 66 | PrincipalRoleRelation.objects.get(user=principal, role=role, content_id=obj.id, content_type=ctype) 67 | except PrincipalRoleRelation.DoesNotExist: 68 | PrincipalRoleRelation.objects.create(user=principal, role=role, content=obj) 69 | return True 70 | else: 71 | try: 72 | PrincipalRoleRelation.objects.get(group=principal, role=role, content_id=obj.id, content_type=ctype) 73 | except PrincipalRoleRelation.DoesNotExist: 74 | PrincipalRoleRelation.objects.create(group=principal, role=role, content=obj) 75 | return True 76 | 77 | return False 78 | 79 | def remove_role(principal, role): 80 | """Removes role from passed principal. 81 | 82 | **Parameters:** 83 | 84 | principal 85 | The principal (user or group) from which the role is removed. 86 | 87 | role 88 | The role which is removed. 89 | """ 90 | try: 91 | if isinstance(principal, User): 92 | ppr = PrincipalRoleRelation.objects.get( 93 | user=principal, role=role, content_id=None, content_type=None) 94 | else: 95 | ppr = PrincipalRoleRelation.objects.get( 96 | group=principal, role=role, content_id=None, content_type=None) 97 | 98 | except PrincipalRoleRelation.DoesNotExist: 99 | return False 100 | else: 101 | ppr.delete() 102 | 103 | return True 104 | 105 | def remove_local_role(obj, principal, role): 106 | """Removes role from passed object and principle. 107 | 108 | **Parameters:** 109 | 110 | obj 111 | The object from which the role is removed. 112 | 113 | principal 114 | The principal (user or group) from which the role is removed. 115 | 116 | role 117 | The role which is removed. 118 | """ 119 | try: 120 | ctype = ContentType.objects.get_for_model(obj) 121 | 122 | if isinstance(principal, User): 123 | ppr = PrincipalRoleRelation.objects.get( 124 | user=principal, role=role, content_id=obj.id, content_type=ctype) 125 | else: 126 | ppr = PrincipalRoleRelation.objects.get( 127 | group=principal, role=role, content_id=obj.id, content_type=ctype) 128 | 129 | except PrincipalRoleRelation.DoesNotExist: 130 | return False 131 | else: 132 | ppr.delete() 133 | 134 | return True 135 | 136 | def remove_roles(principal): 137 | """Removes all roles passed principal (user or group). 138 | 139 | **Parameters:** 140 | 141 | principal 142 | The principal (user or group) from which all roles are removed. 143 | """ 144 | if isinstance(principal, User): 145 | ppr = PrincipalRoleRelation.objects.filter( 146 | user=principal, content_id=None, content_type=None) 147 | else: 148 | ppr = PrincipalRoleRelation.objects.filter( 149 | group=principal, content_id=None, content_type=None) 150 | 151 | if ppr: 152 | ppr.delete() 153 | return True 154 | else: 155 | return False 156 | 157 | def remove_local_roles(obj, principal): 158 | """Removes all local roles from passed object and principal (user or 159 | group). 160 | 161 | **Parameters:** 162 | 163 | obj 164 | The object from which the roles are removed. 165 | 166 | principal 167 | The principal (user or group) from which the roles are removed. 168 | """ 169 | ctype = ContentType.objects.get_for_model(obj) 170 | 171 | if isinstance(principal, User): 172 | ppr = PrincipalRoleRelation.objects.filter( 173 | user=principal, content_id=obj.id, content_type=ctype) 174 | else: 175 | ppr = PrincipalRoleRelation.objects.filter( 176 | group=principal, content_id=obj.id, content_type=ctype) 177 | 178 | if ppr: 179 | ppr.delete() 180 | return True 181 | else: 182 | return False 183 | 184 | 185 | def get_roles(user, obj=None): 186 | """Returns *all* roles of the passed user. 187 | 188 | This takes direct roles and roles via the user's groups into account. 189 | 190 | If an object is passed local roles will also added. Then all local roles 191 | from all ancestors and all user's groups are also taken into account. 192 | 193 | This is the method to use if one want to know whether the passed user 194 | has a role in general (for the passed object). 195 | 196 | **Parameters:** 197 | 198 | user 199 | The user for which the roles are returned. 200 | 201 | obj 202 | The object for which local roles will returned. 203 | 204 | """ 205 | # Cached roles 206 | obj_id = "0" 207 | if obj: 208 | ctype = ContentType.objects.get_for_model(obj) 209 | obj_id = "{}|{}".format(obj.id, ctype.id) 210 | try: 211 | return user.roles[obj_id] 212 | except (AttributeError, KeyError): 213 | pass 214 | 215 | groups = user.groups.all() 216 | groups_ids_str = ", ".join([str(g.id) for g in groups]) 217 | 218 | if groups_ids_str: 219 | prrs = PrincipalRoleRelation.objects.filter( 220 | Q(user_id=user.id) | Q(group_id__in=groups_ids_str), content_id=None 221 | ).values("role_id") 222 | else: 223 | prrs = PrincipalRoleRelation.objects.filter(user_id=user.id, content_id=None).values("role_id") 224 | 225 | role_ids = [ppr["role_id"] for ppr in prrs] 226 | 227 | # Local roles for user and the user's groups and all ancestors of the 228 | # passed object. 229 | while obj: 230 | ctype = ContentType.objects.get_for_model(obj) 231 | 232 | if groups_ids_str: 233 | prrs = PrincipalRoleRelation.objects.filter( 234 | Q(user_id=user.id) | Q(group_id__in=groups_ids_str), content_id=obj.id, content_type_id=ctype.id 235 | ).values("role_id") 236 | else: 237 | prrs = PrincipalRoleRelation.objects.filter( 238 | user_id=user.id, content_id=obj.id, content_type_id=ctype.id 239 | ).values("role_id") 240 | 241 | for prr in prrs: 242 | role_ids.append(prr["role_id"]) 243 | 244 | try: 245 | obj = obj.get_parent_for_permissions() 246 | except AttributeError: 247 | obj = None 248 | 249 | roles = Role.objects.filter(pk__in=role_ids) 250 | 251 | # Cache roles per object 252 | if not getattr(user, "roles", False): 253 | user.roles = {} 254 | user.roles[obj_id] = roles 255 | 256 | return roles 257 | 258 | 259 | def get_global_roles(principal): 260 | """Returns *direct* global roles of passed principal (user or group). 261 | """ 262 | if isinstance(principal, User): 263 | return [prr.role for prr in PrincipalRoleRelation.objects.filter( 264 | user=principal, content_id=None, content_type=None)] 265 | else: 266 | if isinstance(principal, Group): 267 | principal = (principal,) 268 | return [prr.role for prr in PrincipalRoleRelation.objects.filter( 269 | group__in=principal, content_id=None, content_type=None)] 270 | 271 | 272 | def get_local_roles(obj, principal): 273 | """Returns *direct* local roles for passed principal and content object. 274 | """ 275 | ctype = ContentType.objects.get_for_model(obj) 276 | 277 | if isinstance(principal, User): 278 | return [prr.role for prr in PrincipalRoleRelation.objects.filter( 279 | user=principal, content_id=obj.id, content_type=ctype)] 280 | else: 281 | return [prr.role for prr in PrincipalRoleRelation.objects.filter( 282 | group=principal, content_id=obj.id, content_type=ctype)] 283 | 284 | # Permissions ################################################################ 285 | 286 | def check_permission(obj, user, codename, roles=None): 287 | """Checks whether passed user has passed permission for passed obj. 288 | 289 | **Parameters:** 290 | 291 | obj 292 | The object for which the permission should be checked. 293 | 294 | codename 295 | The permission's codename which should be checked. 296 | 297 | user 298 | The user for which the permission should be checked. 299 | 300 | roles 301 | If given these roles will be assigned to the user temporarily before 302 | the permissions are checked. 303 | """ 304 | if not has_permission(obj, user, codename, roles): 305 | raise Unauthorized("User '%s' doesn't have permission '%s' for object '/%s' (%s)." 306 | % (user, codename, obj.slug, obj.__class__.__name__)) 307 | 308 | def grant_permission(obj, role, permission): 309 | """Grants passed permission to passed role. Returns True if the permission 310 | was able to be added, otherwise False. 311 | 312 | **Parameters:** 313 | 314 | obj 315 | The content object for which the permission should be granted. 316 | 317 | role 318 | The role for which the permission should be granted. 319 | 320 | permission 321 | The permission which should be granted. Either a permission 322 | object or the codename of a permission. 323 | """ 324 | if not isinstance(permission, Permission): 325 | try: 326 | permission = Permission.objects.get(codename = permission) 327 | except Permission.DoesNotExist: 328 | return False 329 | 330 | ct = ContentType.objects.get_for_model(obj) 331 | try: 332 | ObjectPermission.objects.get(role=role, content_type = ct, content_id=obj.id, permission=permission) 333 | except ObjectPermission.DoesNotExist: 334 | ObjectPermission.objects.create(role=role, content=obj, permission=permission) 335 | 336 | return True 337 | 338 | def remove_permission(obj, role, permission): 339 | """Removes passed permission from passed role and object. Returns True if 340 | the permission has been removed. 341 | 342 | **Parameters:** 343 | 344 | obj 345 | The content object for which a permission should be removed. 346 | 347 | role 348 | The role for which a permission should be removed. 349 | 350 | permission 351 | The permission which should be removed. Either a permission object 352 | or the codename of a permission. 353 | """ 354 | if not isinstance(permission, Permission): 355 | try: 356 | permission = Permission.objects.get(codename = permission) 357 | except Permission.DoesNotExist: 358 | return False 359 | 360 | ct = ContentType.objects.get_for_model(obj) 361 | 362 | try: 363 | op = ObjectPermission.objects.get(role=role, content_type = ct, content_id=obj.id, permission = permission) 364 | except ObjectPermission.DoesNotExist: 365 | return False 366 | 367 | op.delete() 368 | return True 369 | 370 | def has_permission(obj, user, codename, roles=None): 371 | """Checks whether the passed user has passed permission for passed object. 372 | 373 | **Parameters:** 374 | 375 | obj 376 | The object for which the permission should be checked. 377 | 378 | codename 379 | The permission's codename which should be checked. 380 | 381 | request 382 | The current request. 383 | 384 | roles 385 | If given these roles will be assigned to the user temporarily before 386 | the permissions are checked. 387 | """ 388 | if user.is_superuser: 389 | return True 390 | 391 | if roles is None: 392 | roles = [] 393 | 394 | if not user.is_anonymous(): 395 | roles.extend(get_roles(user, obj)) 396 | 397 | ctype = ContentType.objects.get_for_model(obj) 398 | 399 | result = False 400 | while obj is not None: 401 | p = ObjectPermission.objects.filter( 402 | content_type=ctype, content_id=obj.id, role__in=roles, permission__codename = codename).values("id") 403 | 404 | if len(p) > 0: 405 | result = True 406 | break 407 | 408 | if is_inherited(obj, codename) == False: 409 | result = False 410 | break 411 | 412 | try: 413 | obj = obj.get_parent_for_permissions() 414 | ctype = ContentType.objects.get_for_model(obj) 415 | except AttributeError: 416 | result = False 417 | break 418 | 419 | return result 420 | 421 | # Inheritance ################################################################ 422 | 423 | def add_inheritance_block(obj, permission): 424 | """Adds an inheritance for the passed permission on the passed obj. 425 | 426 | **Parameters:** 427 | 428 | permission 429 | The permission for which an inheritance block should be added. 430 | Either a permission object or the codename of a permission. 431 | obj 432 | The content object for which an inheritance block should be added. 433 | """ 434 | if not isinstance(permission, Permission): 435 | try: 436 | permission = Permission.objects.get(codename = permission) 437 | except Permission.DoesNotExist: 438 | return False 439 | 440 | ct = ContentType.objects.get_for_model(obj) 441 | try: 442 | ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.id, permission=permission) 443 | except ObjectPermissionInheritanceBlock.DoesNotExist: 444 | try: 445 | ObjectPermissionInheritanceBlock.objects.create(content=obj, permission=permission) 446 | except IntegrityError: 447 | return False 448 | return True 449 | 450 | def remove_inheritance_block(obj, permission): 451 | """Removes a inheritance block for the passed permission from the passed 452 | object. 453 | 454 | **Parameters:** 455 | 456 | obj 457 | The content object for which an inheritance block should be added. 458 | 459 | permission 460 | The permission for which an inheritance block should be removed. 461 | Either a permission object or the codename of a permission. 462 | """ 463 | if not isinstance(permission, Permission): 464 | try: 465 | permission = Permission.objects.get(codename = permission) 466 | except Permission.DoesNotExist: 467 | return False 468 | 469 | ct = ContentType.objects.get_for_model(obj) 470 | try: 471 | opi = ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.id, permission=permission) 472 | except ObjectPermissionInheritanceBlock.DoesNotExist: 473 | return False 474 | 475 | opi.delete() 476 | return True 477 | 478 | def is_inherited(obj, codename): 479 | """Returns True if the passed permission is inherited for passed object. 480 | 481 | **Parameters:** 482 | 483 | obj 484 | The content object for which the permission should be checked. 485 | 486 | codename 487 | The permission which should be checked. Must be the codename of the 488 | permission. 489 | """ 490 | ctype = ContentType.objects.get_for_model(obj) 491 | try: 492 | ObjectPermissionInheritanceBlock.objects.get( 493 | content_type=ctype, content_id=obj.id, permission__codename = codename) 494 | except ObjectDoesNotExist: 495 | return True 496 | else: 497 | return False 498 | 499 | def get_group(id_or_name): 500 | """Returns the group with passed id or name. If it not exists it returns 501 | None. 502 | """ 503 | try: 504 | return Group.objects.get(pk=id_or_name) 505 | except (Group.DoesNotExist, ValueError): 506 | try: 507 | return Group.objects.get(name=id_or_name) 508 | except Group.DoesNotExist: 509 | return None 510 | 511 | def get_role(id_or_name): 512 | """Returns the role with passed id or name. If it not exists it returns 513 | None. 514 | 515 | **Parameters:** 516 | 517 | id_or_name 518 | The id or the name of the role which should be returned. 519 | """ 520 | try: 521 | return Role.objects.get(pk=id_or_name) 522 | except (Role.DoesNotExist, ValueError): 523 | try: 524 | return Role.objects.get(name=id_or_name) 525 | except Role.DoesNotExist: 526 | return None 527 | 528 | def get_user(id_or_username): 529 | """Returns the user with passed id or username. If it not exists it returns 530 | None. 531 | 532 | **Parameters:** 533 | 534 | id_or_username 535 | The id or the username of the user which should be returned. 536 | """ 537 | try: 538 | return User.objects.get(pk=id_or_username) 539 | except (User.DoesNotExist, ValueError): 540 | try: 541 | return User.objects.get(username=id_or_username) 542 | except User.DoesNotExist: 543 | return None 544 | 545 | def has_group(user, group): 546 | """Returns True if passed user has passed group. 547 | """ 548 | if isinstance(group, str): 549 | group = Group.objects.get(name=group) 550 | 551 | return group in user.groups.all() 552 | 553 | def reset(obj): 554 | """Resets all permissions and inheritance blocks of passed object. 555 | """ 556 | ctype = ContentType.objects.get_for_model(obj) 557 | ObjectPermissionInheritanceBlock.objects.filter(content_id=obj.id, content_type=ctype).delete() 558 | ObjectPermission.objects.filter(content_id=obj.id, content_type=ctype).delete() 559 | 560 | # Registering ################################################################ 561 | 562 | def register_permission(name, codename, ctypes=None): 563 | """Registers a permission to the framework. Returns the permission if the 564 | registration was successfully, otherwise False. 565 | 566 | **Parameters:** 567 | 568 | name 569 | The unique name of the permission. This is displayed to the customer. 570 | 571 | codename 572 | The unique codename of the permission. This is used internally to 573 | identify the permission. 574 | 575 | content_types 576 | The content type for which the permission is active. This can be 577 | used to display only reasonable permissions for an object. This 578 | must be a Django ContentType 579 | """ 580 | if ctypes is None: 581 | ctypes = [] 582 | 583 | # Permission with same codename and/or name must not exist. 584 | if Permission.objects.filter(Q(name=name) | Q(codename=codename)): 585 | return False 586 | 587 | p = Permission.objects.create(name=name, codename=codename) 588 | 589 | ctypes = [ContentType.objects.get_for_model(ctype) for ctype in ctypes] 590 | if ctypes: 591 | p.content_types = ctypes 592 | p.save() 593 | 594 | return p 595 | 596 | def unregister_permission(codename): 597 | """Unregisters a permission from the framework 598 | 599 | **Parameters:** 600 | 601 | codename 602 | The unique codename of the permission. 603 | """ 604 | try: 605 | permission = Permission.objects.get(codename=codename) 606 | except Permission.DoesNotExist: 607 | return False 608 | permission.delete() 609 | return True 610 | 611 | def register_role(name): 612 | """Registers a role with passed name to the framework. Returns the new 613 | role if the registration was successfully, otherwise False. 614 | 615 | **Parameters:** 616 | 617 | name 618 | The unique role name. 619 | """ 620 | role, created = Role.objects.get_or_create(name=name) 621 | if created: 622 | return role 623 | else: 624 | return False 625 | 626 | def unregister_role(name): 627 | """Unregisters the role with passed name. 628 | 629 | **Parameters:** 630 | 631 | name 632 | The unique role name. 633 | """ 634 | try: 635 | role = Role.objects.get(name=name) 636 | except Role.DoesNotExist: 637 | return False 638 | 639 | role.delete() 640 | return True 641 | 642 | def register_group(name): 643 | """Registers a group with passed name to the framework. Returns the new 644 | group if the registration was successfully, otherwise False. 645 | 646 | Actually this creates just a default Django Group. 647 | 648 | **Parameters:** 649 | 650 | name 651 | The unique group name. 652 | """ 653 | group, created = Group.objects.get_or_create(name=name) 654 | if created: 655 | return group 656 | else: 657 | return False 658 | 659 | def unregister_group(name): 660 | """Unregisters the group with passed name. Returns True if the 661 | unregistration was succesfull otherwise False. 662 | 663 | Actually this deletes just a default Django Group. 664 | 665 | **Parameters:** 666 | 667 | name 668 | The unique role name. 669 | """ 670 | try: 671 | group = Group.objects.get(name=name) 672 | except Group.DoesNotExist: 673 | return False 674 | 675 | group.delete() 676 | return True 677 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | version = 'dev' 5 | 6 | here = os.path.abspath(os.path.dirname(__file__)) 7 | README = open(os.path.join(here, 'README.txt')).read() 8 | 9 | setup( 10 | name='django-permissions', 11 | version=version, 12 | description='Generic per-object permissions for Django', 13 | long_description=README, 14 | classifiers=[ 15 | 'Development Status :: 5 - Production/Stable', 16 | 'Environment :: Web Environment', 17 | 'Framework :: Django', 18 | 'License :: OSI Approved :: BSD License', 19 | 'Operating System :: OS Independent', 20 | 'Programming Language :: Python', 21 | ], 22 | keywords='django authorization permissions', 23 | author='Kai Diefenbach', 24 | author_email='kai.diefenbach@iqpp.de', 25 | url='http://www.iqpp.com', 26 | license='BSD', 27 | packages=find_packages(exclude=['ez_setup']), 28 | include_package_data=True, 29 | zip_safe=False, 30 | install_requires=[ 31 | 'setuptools', 32 | ], 33 | ) 34 | --------------------------------------------------------------------------------