mirror of
https://github.com/jazzband/django-authority.git
synced 2026-04-26 17:54:46 +00:00
Compare commits
No commits in common. "master" and "0.8" have entirely different histories.
60 changed files with 1051 additions and 1457 deletions
|
|
@ -1,6 +0,0 @@
|
||||||
[run]
|
|
||||||
source = authority
|
|
||||||
branch = 1
|
|
||||||
|
|
||||||
[report]
|
|
||||||
omit = *tests*,*migrations*
|
|
||||||
53
.github/workflows/release.yml
vendored
53
.github/workflows/release.yml
vendored
|
|
@ -1,53 +0,0 @@
|
||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
if: github.repository == 'jazzband/django-authority'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: 3.8
|
|
||||||
|
|
||||||
- name: Get pip cache dir
|
|
||||||
id: pip-cache
|
|
||||||
run: |
|
|
||||||
echo "::set-output name=dir::$(pip cache dir)"
|
|
||||||
|
|
||||||
- name: Cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pip-cache.outputs.dir }}
|
|
||||||
key: release-${{ hashFiles('**/setup.py') }}
|
|
||||||
restore-keys: |
|
|
||||||
release-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install -U pip
|
|
||||||
python -m pip install -U setuptools twine wheel
|
|
||||||
|
|
||||||
- name: Build package
|
|
||||||
run: |
|
|
||||||
python setup.py --version
|
|
||||||
python setup.py sdist --format=gztar bdist_wheel
|
|
||||||
twine check dist/*
|
|
||||||
|
|
||||||
- name: Upload packages to Jazzband
|
|
||||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
|
||||||
uses: pypa/gh-action-pypi-publish@master
|
|
||||||
with:
|
|
||||||
user: jazzband
|
|
||||||
password: ${{ secrets.JAZZBAND_RELEASE_KEY }}
|
|
||||||
repository_url: https://jazzband.co/projects/django-authority/upload
|
|
||||||
48
.github/workflows/test.yml
vendored
48
.github/workflows/test.yml
vendored
|
|
@ -1,48 +0,0 @@
|
||||||
name: Test
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
max-parallel: 5
|
|
||||||
matrix:
|
|
||||||
python-version: ['2.7', '3.6', '3.7', '3.8']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Get pip cache dir
|
|
||||||
id: pip-cache
|
|
||||||
run: |
|
|
||||||
echo "::set-output name=dir::$(pip cache dir)"
|
|
||||||
|
|
||||||
- name: Cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pip-cache.outputs.dir }}
|
|
||||||
key:
|
|
||||||
${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ matrix.python-version }}-v1-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
python -m pip install --upgrade tox tox-gh-actions
|
|
||||||
|
|
||||||
- name: Tox tests
|
|
||||||
run: |
|
|
||||||
tox -v
|
|
||||||
|
|
||||||
- name: Upload coverage
|
|
||||||
uses: codecov/codecov-action@v1
|
|
||||||
with:
|
|
||||||
name: Python ${{ matrix.python-version }}
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -2,9 +2,4 @@
|
||||||
*.egg-info
|
*.egg-info
|
||||||
*.sql
|
*.sql
|
||||||
docs/build/*
|
docs/build/*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.tox/
|
|
||||||
dist/
|
|
||||||
build/
|
|
||||||
.coverage
|
|
||||||
.eggs/
|
|
||||||
26
.hgignore
Normal file
26
.hgignore
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
syntax: glob
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
*.orig
|
||||||
|
*.kpf
|
||||||
|
*.egg-info
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
.DS_Store
|
||||||
|
MANIFEST
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
dev.db
|
||||||
|
local_settings.py
|
||||||
|
parts/*
|
||||||
|
eggs/*
|
||||||
|
downloads/*
|
||||||
|
.installed.cfg
|
||||||
|
bin/*
|
||||||
|
develop-eggs/*.egg-link
|
||||||
|
example/example.db
|
||||||
|
docs/build
|
||||||
|
TODO
|
||||||
|
example/example.db
|
||||||
22
.travis.yml
Normal file
22
.travis.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
language: python
|
||||||
|
python:
|
||||||
|
- "2.6"
|
||||||
|
- "2.7"
|
||||||
|
- "3.3"
|
||||||
|
script: ./example/manage.py test authority
|
||||||
|
env:
|
||||||
|
- TRAVIS_DJANGO_VERSION=1.3
|
||||||
|
- TRAVIS_DJANGO_VERSION=1.4
|
||||||
|
- TRAVIS_DJANGO_VERSION=1.5
|
||||||
|
- TRAVIS_DJANGO_VERSION=1.6
|
||||||
|
install:
|
||||||
|
- pip install django==$TRAVIS_DJANGO_VERSION --use-mirrors
|
||||||
|
matrix:
|
||||||
|
exclude:
|
||||||
|
- python: "3.3"
|
||||||
|
env: TRAVIS_DJANGO_VERSION=1.3
|
||||||
|
- python: "3.3"
|
||||||
|
env: TRAVIS_DJANGO_VERSION=1.4
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
- jason.louard.ward@gmail.com
|
||||||
2
AUTHORS
2
AUTHORS
|
|
@ -7,5 +7,3 @@ Kyle Gibson <kyle.gibson@policystat.com>
|
||||||
Jason Ward <jason.ward@policystat.com>
|
Jason Ward <jason.ward@policystat.com>
|
||||||
Travis Chase <http://www.supercodepoet.com/>
|
Travis Chase <http://www.supercodepoet.com/>
|
||||||
Remigiusz Dymecki <remigiusz@gmail.com>
|
Remigiusz Dymecki <remigiusz@gmail.com>
|
||||||
Gunnlaugur Thor Briem <https://github.com/gthb>
|
|
||||||
Bob Cribbs <https://github.com/bocribbz>
|
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# Code of Conduct
|
|
||||||
|
|
||||||
As contributors and maintainers of the Jazzband projects, and in the interest of
|
|
||||||
fostering an open and welcoming community, we pledge to respect all people who
|
|
||||||
contribute through reporting issues, posting feature requests, updating documentation,
|
|
||||||
submitting pull requests or patches, and other activities.
|
|
||||||
|
|
||||||
We are committed to making participation in the Jazzband a harassment-free experience
|
|
||||||
for everyone, regardless of the level of experience, gender, gender identity and
|
|
||||||
expression, sexual orientation, disability, personal appearance, body size, race,
|
|
||||||
ethnicity, age, religion, or nationality.
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
- The use of sexualized language or imagery
|
|
||||||
- Personal attacks
|
|
||||||
- Trolling or insulting/derogatory comments
|
|
||||||
- Public or private harassment
|
|
||||||
- Publishing other's private information, such as physical or electronic addresses,
|
|
||||||
without explicit permission
|
|
||||||
- Other unethical or unprofessional conduct
|
|
||||||
|
|
||||||
The Jazzband roadies have the right and responsibility to remove, edit, or reject
|
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are not
|
|
||||||
aligned to this Code of Conduct, or to ban temporarily or permanently any contributor
|
|
||||||
for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
By adopting this Code of Conduct, the roadies commit themselves to fairly and
|
|
||||||
consistently applying these principles to every aspect of managing the jazzband
|
|
||||||
projects. Roadies who do not follow or enforce the Code of Conduct may be permanently
|
|
||||||
removed from the Jazzband roadies.
|
|
||||||
|
|
||||||
This code of conduct applies both within project spaces and in public spaces when an
|
|
||||||
individual is representing the project or its community.
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
|
||||||
contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and
|
|
||||||
investigated and will result in a response that is deemed necessary and appropriate to
|
|
||||||
the circumstances. Roadies are obligated to maintain confidentiality with regard to the
|
|
||||||
reporter of an incident.
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version
|
|
||||||
1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version]
|
|
||||||
|
|
||||||
[homepage]: https://contributor-covenant.org
|
|
||||||
[version]: https://contributor-covenant.org/version/1/3/0/
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
[](https://jazzband.co/)
|
|
||||||
|
|
||||||
This is a [Jazzband](https://jazzband.co/) project.
|
|
||||||
By contributing you agree to abide by the [Contributor Code of Conduct](https://jazzband.co/about/conduct)
|
|
||||||
and follow the [guidelines](https://jazzband.co/about/guidelines).
|
|
||||||
4
LICENSE
4
LICENSE
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2009-2020, Jannis Leidel
|
Copyright (c) 2009, Jannis Leidel
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
@ -25,4 +25,4 @@ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
include AUTHORS
|
include AUTHORS
|
||||||
include LICENSE
|
include LICENSE
|
||||||
include README.rst
|
include README.rst
|
||||||
|
include buildout.cfg
|
||||||
|
include bootstrap.py
|
||||||
recursive-include authority/templates/authority *.html
|
recursive-include authority/templates/authority *.html
|
||||||
recursive-include authority/templates/admin *.html
|
recursive-include authority/templates/admin *.html
|
||||||
recursive-include authority/fixtures *.json
|
recursive-include authority/fixtures *.json
|
||||||
|
|
|
||||||
106
README.rst
106
README.rst
|
|
@ -2,17 +2,6 @@
|
||||||
django-authority
|
django-authority
|
||||||
================
|
================
|
||||||
|
|
||||||
.. image:: https://jazzband.co/static/img/badge.svg
|
|
||||||
:target: https://jazzband.co/
|
|
||||||
:alt: Jazzband
|
|
||||||
|
|
||||||
.. image:: https://github.com/jazzband/django-authority/workflows/Test/badge.svg
|
|
||||||
:target: https://github.com/jazzband/django-authority/actions
|
|
||||||
:alt: GitHub Actions
|
|
||||||
|
|
||||||
.. image:: https://codecov.io/gh/jazzband/django-authority/branch/master/graph/badge.svg
|
|
||||||
:target: https://codecov.io/gh/jazzband/django-authority
|
|
||||||
|
|
||||||
This is a Django app for per-object-permissions that includes a bunch of
|
This is a Django app for per-object-permissions that includes a bunch of
|
||||||
helpers to create custom permission checks.
|
helpers to create custom permission checks.
|
||||||
|
|
||||||
|
|
@ -21,31 +10,36 @@ The main website for django-authority is
|
||||||
`in-development version`_ of django-authority with
|
`in-development version`_ of django-authority with
|
||||||
``pip install django-authority==dev`` or ``easy_install django-authority==dev``.
|
``pip install django-authority==dev`` or ``easy_install django-authority==dev``.
|
||||||
|
|
||||||
.. _`django-authority.readthedocs.org`: https://django-authority.readthedocs.io/
|
.. _`django-authority.readthedocs.org`: http://django-authority.readthedocs.org/
|
||||||
.. _in-development version: https://github.com/jazzband/django-authority/archive/master.zip#egg=django-authority-dev
|
.. _in-development version: https://github.com/jezdez/django-authority/archive/master.zip#egg=django-authority-dev
|
||||||
|
|
||||||
Example
|
Example
|
||||||
=======
|
=======
|
||||||
|
|
||||||
To get the example project running do:
|
To get the example project running do:
|
||||||
|
|
||||||
- Bootstrap the environment by running in a virtualenv::
|
- Bootstrap the buildout by running::
|
||||||
|
|
||||||
pip install Django
|
python bootstrap.py
|
||||||
pip install -e .
|
|
||||||
|
- Get the required packages by running::
|
||||||
|
|
||||||
|
bin/buildout
|
||||||
|
|
||||||
- Sync the database::
|
- Sync the database::
|
||||||
|
|
||||||
python example/manage.py migrate
|
bin/django-trunk syncdb
|
||||||
|
|
||||||
- Run the development server and visit the admin at http://127.0.0.1:8000/admin/::
|
- Run the development server and visit the admin at http://127.0.0.1:8000/admin/::
|
||||||
|
|
||||||
python example/manage.py runserver
|
bin/django-trunk runserver
|
||||||
|
|
||||||
Now create a flatage and open it to see some of the templatetags in action.
|
Now create a flatage and open it to see some of the templatetags in action.
|
||||||
Don't hesitate to use the admin to edit the permission objects.
|
Don't hesitate to use the admin to edit the permission objects.
|
||||||
|
|
||||||
Please use https://github.com/jazzband/django-authority/issues/ for issues and bug reports.
|
Full docs coming soon.
|
||||||
|
|
||||||
|
Please use https://github.com/jezdez/django-authority/issues/ for issues and bug reports.
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
=============
|
=============
|
||||||
|
|
@ -58,66 +52,6 @@ html version using the setup.py::
|
||||||
Changelog:
|
Changelog:
|
||||||
==========
|
==========
|
||||||
|
|
||||||
0.15 (unreleased):
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* Moved CI to GitHub Actions.
|
|
||||||
* Add Django 3.0 and 3.1 support.
|
|
||||||
* Add Python 3.6 and 3.8 support.
|
|
||||||
|
|
||||||
0.14 (2020-02-07):
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* Add Django 2.2 support
|
|
||||||
* Add Python 3.7 support
|
|
||||||
* Various fixes around the test harness.
|
|
||||||
* Use Django's own method of auto-loading permissions modules.
|
|
||||||
* Fix Django admin incompatibility regarding a method removed years ago.
|
|
||||||
* Removed unused compatibility code.
|
|
||||||
* Fix BasePermission.assign for group permissions.
|
|
||||||
|
|
||||||
0.13.1 (2018-01-28):
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
* Minor fixes to the documentation and versioning.
|
|
||||||
|
|
||||||
0.13 (2018-01-28):
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* Added support for Django 1.11
|
|
||||||
* Drop Support for Python 3.3
|
|
||||||
* Fixed a bug with template loader
|
|
||||||
|
|
||||||
0.12 (2017-01-10):
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* Added support for Django 1.10
|
|
||||||
|
|
||||||
0.11 (2016-07-17):
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* Added Migration in order to support Django 1.8
|
|
||||||
|
|
||||||
* Dropped Support for Django 1.7 and lower
|
|
||||||
|
|
||||||
* Remove SQL Migration Files
|
|
||||||
|
|
||||||
* Documentation Updates
|
|
||||||
|
|
||||||
* Fix linter issues
|
|
||||||
|
|
||||||
0.10 (2015-12-14):
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* Fixed a bug with BasePermissionForm and django 1.8
|
|
||||||
|
|
||||||
0.9 (2015-11-11):
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
* Added support for Django 1.7 and 1.8
|
|
||||||
|
|
||||||
* Dropped support for Django 1.3
|
|
||||||
|
|
||||||
0.8 (2013-12-20):
|
0.8 (2013-12-20):
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
@ -153,9 +87,7 @@ Changelog:
|
||||||
|
|
||||||
* Added ability to override form class in ``add_permission`` view.
|
* Added ability to override form class in ``add_permission`` view.
|
||||||
|
|
||||||
* Added easy way to assign permissions via a permission instance, e.g.:
|
* Added easy way to assign permissions via a permission instance, e.g.::
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from mysite.articles.permissions import ArticlePermission
|
from mysite.articles.permissions import ArticlePermission
|
||||||
|
|
@ -183,17 +115,13 @@ Changelog:
|
||||||
* The templatetags have also been refactored to be easier to customize
|
* The templatetags have also been refactored to be easier to customize
|
||||||
which required a change in the template tag signature:
|
which required a change in the template tag signature:
|
||||||
|
|
||||||
Old:
|
Old::
|
||||||
|
|
||||||
.. code-block:: html+django
|
|
||||||
|
|
||||||
{% permission_form flatpage %}
|
{% permission_form flatpage %}
|
||||||
{% permission_form flatpage "flatpage_permission.top_secret" %}
|
{% permission_form flatpage "flatpage_permission.top_secret" %}
|
||||||
{% permission_form OBJ PERMISSION_LABEL.CHECK_NAME %}
|
{% permission_form OBJ PERMISSION_LABEL.CHECK_NAME %}
|
||||||
|
|
||||||
New:
|
New::
|
||||||
|
|
||||||
.. code-block:: html+django
|
|
||||||
|
|
||||||
{% permission_form for flatpage %}
|
{% permission_form for flatpage %}
|
||||||
{% permission_form for flatpage using "flatpage_permission.top_secret" %}
|
{% permission_form for flatpage using "flatpage_permission.top_secret" %}
|
||||||
|
|
@ -212,4 +140,4 @@ Changelog:
|
||||||
allows to request permissions, but also add them (only for users with
|
allows to request permissions, but also add them (only for users with
|
||||||
the 'authority.add_permission' Django permission).
|
the 'authority.add_permission' Django permission).
|
||||||
|
|
||||||
.. _`migrations/`: https://github.com/jazzbands/django-authority/tree/master/migrations
|
.. _`migrations/`: https://github.com/jezdez/django-authority/tree/master/migrations
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,8 @@
|
||||||
from pkg_resources import get_distribution, DistributionNotFound
|
import sys
|
||||||
|
from authority.sites import site, get_check, get_choices_for, register, unregister
|
||||||
try:
|
|
||||||
__version__ = get_distribution("django-authority").version
|
|
||||||
except DistributionNotFound:
|
|
||||||
# package is not installed
|
|
||||||
pass
|
|
||||||
|
|
||||||
LOADING = False
|
LOADING = False
|
||||||
|
|
||||||
|
|
||||||
def autodiscover():
|
def autodiscover():
|
||||||
"""
|
"""
|
||||||
Goes and imports the permissions submodule of every app in INSTALLED_APPS
|
Goes and imports the permissions submodule of every app in INSTALLED_APPS
|
||||||
|
|
@ -19,6 +13,19 @@ def autodiscover():
|
||||||
return
|
return
|
||||||
LOADING = True
|
LOADING = True
|
||||||
|
|
||||||
from django.utils.module_loading import autodiscover_modules
|
import imp
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
autodiscover_modules("permissions")
|
for app in settings.INSTALLED_APPS:
|
||||||
|
try:
|
||||||
|
__import__(app)
|
||||||
|
app_path = sys.modules[app].__path__
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
imp.find_module('permissions', app_path)
|
||||||
|
except ImportError:
|
||||||
|
continue
|
||||||
|
__import__("%s.permissions" % app)
|
||||||
|
app_path = sys.modules["%s.permissions" % app]
|
||||||
|
LOADING = False
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
from django import forms
|
from django import forms, template
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.translation import ugettext, ungettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ungettext, ugettext_lazy as _
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render_to_response
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.forms.formsets import all_valid
|
from django.forms.formsets import all_valid
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.admin import actions, helpers
|
from django.contrib.admin import helpers
|
||||||
from django.contrib.contenttypes.admin import GenericTabularInline
|
from django.contrib.contenttypes import generic
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
|
|
@ -15,48 +15,47 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from django.utils.encoding import force_unicode as force_text
|
from django.utils.encoding import force_unicode as force_text
|
||||||
|
|
||||||
|
try:
|
||||||
|
from django.contrib.admin import actions
|
||||||
|
except ImportError:
|
||||||
|
actions = False
|
||||||
|
|
||||||
from authority.models import Permission
|
from authority.models import Permission
|
||||||
from authority.widgets import GenericForeignKeyRawIdWidget
|
from authority.widgets import GenericForeignKeyRawIdWidget
|
||||||
from authority.utils import get_choices_for
|
from authority import get_choices_for
|
||||||
|
|
||||||
|
class PermissionInline(generic.GenericTabularInline):
|
||||||
class PermissionInline(GenericTabularInline):
|
|
||||||
model = Permission
|
model = Permission
|
||||||
raw_id_fields = ("user", "group", "creator")
|
raw_id_fields = ('user', 'group', 'creator')
|
||||||
extra = 1
|
extra = 1
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
if db_field.name == "codename":
|
if db_field.name == 'codename':
|
||||||
perm_choices = get_choices_for(self.parent_model)
|
perm_choices = get_choices_for(self.parent_model)
|
||||||
kwargs["label"] = _("permission")
|
kwargs['label'] = _('permission')
|
||||||
kwargs["widget"] = forms.Select(choices=perm_choices)
|
kwargs['widget'] = forms.Select(choices=perm_choices)
|
||||||
|
return db_field.formfield(**kwargs)
|
||||||
return super(PermissionInline, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(PermissionInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ActionPermissionInline(PermissionInline):
|
class ActionPermissionInline(PermissionInline):
|
||||||
raw_id_fields = ()
|
raw_id_fields = ()
|
||||||
template = "admin/edit_inline/action_tabular.html"
|
template = 'admin/edit_inline/action_tabular.html'
|
||||||
|
|
||||||
|
class ActionErrorList(forms.util.ErrorList):
|
||||||
class ActionErrorList(forms.utils.ErrorList):
|
|
||||||
def __init__(self, inline_formsets):
|
def __init__(self, inline_formsets):
|
||||||
super(ActionErrorList, self).__init__()
|
|
||||||
for inline_formset in inline_formsets:
|
for inline_formset in inline_formsets:
|
||||||
self.extend(inline_formset.non_form_errors())
|
self.extend(inline_formset.non_form_errors())
|
||||||
for errors_in_inline_form in inline_formset.errors:
|
for errors_in_inline_form in inline_formset.errors:
|
||||||
self.extend(errors_in_inline_form.values())
|
self.extend(errors_in_inline_form.values())
|
||||||
|
|
||||||
|
|
||||||
def edit_permissions(modeladmin, request, queryset):
|
def edit_permissions(modeladmin, request, queryset):
|
||||||
opts = modeladmin.model._meta
|
opts = modeladmin.model._meta
|
||||||
app_label = opts.app_label
|
app_label = opts.app_label
|
||||||
|
|
||||||
# Check that the user has the permission to edit permissions
|
# Check that the user has the permission to edit permissions
|
||||||
if not (
|
if not (request.user.is_superuser or
|
||||||
request.user.is_superuser
|
request.user.has_perm('authority.change_permission') or
|
||||||
or request.user.has_perm("authority.change_permission")
|
request.user.has_perm('authority.change_foreign_permissions')):
|
||||||
or request.user.has_perm("authority.change_foreign_permissions")
|
|
||||||
):
|
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
inline = ActionPermissionInline(queryset.model, modeladmin.admin_site)
|
inline = ActionPermissionInline(queryset.model, modeladmin.admin_site)
|
||||||
|
|
@ -67,11 +66,10 @@ def edit_permissions(modeladmin, request, queryset):
|
||||||
prefix = "%s-%s" % (FormSet.get_default_prefix(), obj.pk)
|
prefix = "%s-%s" % (FormSet.get_default_prefix(), obj.pk)
|
||||||
prefixes[prefix] = prefixes.get(prefix, 0) + 1
|
prefixes[prefix] = prefixes.get(prefix, 0) + 1
|
||||||
if prefixes[prefix] != 1:
|
if prefixes[prefix] != 1:
|
||||||
prefix = "%s-%s" % (prefix, prefixes[prefix])
|
prefix = "%s-%s-%s" % (prefix, prefixes[prefix])
|
||||||
if request.POST.get("post"):
|
if request.POST.get('post'):
|
||||||
formset = FormSet(
|
formset = FormSet(data=request.POST, files=request.FILES,
|
||||||
data=request.POST, files=request.FILES, instance=obj, prefix=prefix
|
instance=obj, prefix=prefix)
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
formset = FormSet(instance=obj, prefix=prefix)
|
formset = FormSet(instance=obj, prefix=prefix)
|
||||||
formsets.append(formset)
|
formsets.append(formset)
|
||||||
|
|
@ -84,105 +82,81 @@ def edit_permissions(modeladmin, request, queryset):
|
||||||
inline_admin_formsets.append(inline_admin_formset)
|
inline_admin_formsets.append(inline_admin_formset)
|
||||||
media = media + inline_admin_formset.media
|
media = media + inline_admin_formset.media
|
||||||
|
|
||||||
if request.POST.get("post"):
|
ordered_objects = opts.get_ordered_objects()
|
||||||
|
if request.POST.get('post'):
|
||||||
if all_valid(formsets):
|
if all_valid(formsets):
|
||||||
for formset in formsets:
|
for formset in formsets:
|
||||||
formset.save()
|
formset.save()
|
||||||
else:
|
|
||||||
modeladmin.message_user(
|
|
||||||
request,
|
|
||||||
"; ".join(
|
|
||||||
err.as_text() for formset in formsets for err in formset.errors
|
|
||||||
),
|
|
||||||
)
|
|
||||||
# redirect to full request path to make sure we keep filter
|
# redirect to full request path to make sure we keep filter
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"errors": ActionErrorList(formsets),
|
'errors': ActionErrorList(formsets),
|
||||||
"title": ugettext("Permissions for %s") % force_text(opts.verbose_name_plural),
|
'title': ugettext('Permissions for %s') % force_text(opts.verbose_name_plural),
|
||||||
"inline_admin_formsets": inline_admin_formsets,
|
'inline_admin_formsets': inline_admin_formsets,
|
||||||
"app_label": app_label,
|
'app_label': app_label,
|
||||||
"change": True,
|
'change': True,
|
||||||
"form_url": mark_safe(""),
|
'ordered_objects': ordered_objects,
|
||||||
"opts": opts,
|
'form_url': mark_safe(''),
|
||||||
"target_opts": queryset.model._meta,
|
'opts': opts,
|
||||||
"content_type_id": ContentType.objects.get_for_model(queryset.model).id,
|
'target_opts': queryset.model._meta,
|
||||||
"save_as": False,
|
'content_type_id': ContentType.objects.get_for_model(queryset.model).id,
|
||||||
"save_on_top": False,
|
'save_as': False,
|
||||||
"is_popup": False,
|
'save_on_top': False,
|
||||||
"media": mark_safe(media),
|
'is_popup': False,
|
||||||
"show_delete": False,
|
'media': mark_safe(media),
|
||||||
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
|
'show_delete': False,
|
||||||
"queryset": queryset,
|
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
|
||||||
|
'queryset': queryset,
|
||||||
"object_name": force_text(opts.verbose_name),
|
"object_name": force_text(opts.verbose_name),
|
||||||
}
|
}
|
||||||
template_name = getattr(
|
template_name = getattr(modeladmin, 'permission_change_form_template', [
|
||||||
modeladmin,
|
"admin/%s/%s/permission_change_form.html" % (app_label, opts.object_name.lower()),
|
||||||
"permission_change_form_template",
|
"admin/%s/permission_change_form.html" % app_label,
|
||||||
[
|
"admin/permission_change_form.html"
|
||||||
"admin/%s/%s/permission_change_form.html"
|
])
|
||||||
% (app_label, opts.object_name.lower()),
|
return render_to_response(template_name, context,
|
||||||
"admin/%s/permission_change_form.html" % app_label,
|
context_instance=template.RequestContext(request))
|
||||||
"admin/permission_change_form.html",
|
edit_permissions.short_description = _("Edit permissions for selected %(verbose_name_plural)s")
|
||||||
],
|
|
||||||
)
|
|
||||||
return render(request, template_name, context)
|
|
||||||
|
|
||||||
|
|
||||||
edit_permissions.short_description = _(
|
|
||||||
"Edit permissions for selected %(verbose_name_plural)s"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PermissionAdmin(admin.ModelAdmin):
|
class PermissionAdmin(admin.ModelAdmin):
|
||||||
list_display = ("codename", "content_type", "user", "group", "approved")
|
list_display = ('codename', 'content_type', 'user', 'group', 'approved')
|
||||||
list_filter = ("approved", "content_type")
|
list_filter = ('approved', 'content_type')
|
||||||
search_fields = ("user__username", "group__name", "codename")
|
search_fields = ('user__username', 'group__name', 'codename')
|
||||||
raw_id_fields = ("user", "group", "creator")
|
raw_id_fields = ('user', 'group', 'creator')
|
||||||
generic_fields = ("content_object",)
|
generic_fields = ('content_object',)
|
||||||
actions = ["approve_permissions"]
|
actions = ['approve_permissions']
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {"fields": ("codename", ("content_type", "object_id"))}),
|
(None, {'fields': ('codename', ('content_type', 'object_id'))}),
|
||||||
(_("Permitted"), {"fields": ("approved", "user", "group")}),
|
(_('Permitted'), {'fields': ('approved', 'user', 'group')}),
|
||||||
(_("Creation"), {"fields": ("creator", "date_requested", "date_approved")}),
|
(_('Creation'), {'fields': ('creator', 'date_requested', 'date_approved')}),
|
||||||
)
|
)
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
# For generic foreign keys marked as generic_fields we use a special widget
|
# For generic foreign keys marked as generic_fields we use a special widget
|
||||||
names = [
|
if db_field.name in [f.fk_field for f in self.model._meta.virtual_fields if f.name in self.generic_fields]:
|
||||||
f.fk_field
|
|
||||||
for f in self.model._meta.virtual_fields
|
|
||||||
if f.name in self.generic_fields
|
|
||||||
]
|
|
||||||
if db_field.name in names:
|
|
||||||
for gfk in self.model._meta.virtual_fields:
|
for gfk in self.model._meta.virtual_fields:
|
||||||
if gfk.fk_field == db_field.name:
|
if gfk.fk_field == db_field.name:
|
||||||
kwargs["widget"] = GenericForeignKeyRawIdWidget(
|
return db_field.formfield(
|
||||||
gfk.ct_field, self.admin_site._registry.keys()
|
widget=GenericForeignKeyRawIdWidget(
|
||||||
)
|
gfk.ct_field, self.admin_site._registry.keys()))
|
||||||
break
|
|
||||||
return super(PermissionAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(PermissionAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
def queryset(self, request):
|
def queryset(self, request):
|
||||||
user = request.user
|
user = request.user
|
||||||
if user.is_superuser or user.has_perm("permissions.change_foreign_permissions"):
|
if (user.is_superuser or
|
||||||
|
user.has_perm('permissions.change_foreign_permissions')):
|
||||||
return super(PermissionAdmin, self).queryset(request)
|
return super(PermissionAdmin, self).queryset(request)
|
||||||
return super(PermissionAdmin, self).queryset(request).filter(creator=user)
|
return super(PermissionAdmin, self).queryset(request).filter(creator=user)
|
||||||
|
|
||||||
def approve_permissions(self, request, queryset):
|
def approve_permissions(self, request, queryset):
|
||||||
for permission in queryset:
|
for permission in queryset:
|
||||||
permission.approve(request.user)
|
permission.approve(request.user)
|
||||||
message = ungettext(
|
message = ungettext("%(count)d permission successfully approved.",
|
||||||
"%(count)d permission successfully approved.",
|
"%(count)d permissions successfully approved.", len(queryset))
|
||||||
"%(count)d permissions successfully approved.",
|
self.message_user(request, message % {'count': len(queryset)})
|
||||||
len(queryset),
|
|
||||||
)
|
|
||||||
self.message_user(request, message % {"count": len(queryset)})
|
|
||||||
|
|
||||||
approve_permissions.short_description = _("Approve selected permissions")
|
approve_permissions.short_description = _("Approve selected permissions")
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Permission, PermissionAdmin)
|
admin.site.register(Permission, PermissionAdmin)
|
||||||
|
|
||||||
if actions:
|
if actions:
|
||||||
|
|
|
||||||
|
|
@ -2,34 +2,25 @@ import inspect
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.http import urlquote
|
from django.utils.http import urlquote
|
||||||
from django.utils.functional import wraps
|
from django.utils.functional import wraps
|
||||||
from django.db.models import Model
|
from django.db.models import Model, get_model
|
||||||
from django.apps import apps
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
|
|
||||||
from authority.utils import get_check
|
from authority import get_check
|
||||||
from authority.views import permission_denied
|
from authority.views import permission_denied
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
basestring
|
|
||||||
except NameError:
|
|
||||||
basestring = str
|
|
||||||
|
|
||||||
|
|
||||||
def permission_required(perm, *lookup_variables, **kwargs):
|
def permission_required(perm, *lookup_variables, **kwargs):
|
||||||
"""
|
"""
|
||||||
Decorator for views that checks whether a user has a particular permission
|
Decorator for views that checks whether a user has a particular permission
|
||||||
enabled, redirecting to the log-in page if necessary.
|
enabled, redirecting to the log-in page if necessary.
|
||||||
"""
|
"""
|
||||||
login_url = kwargs.pop("login_url", settings.LOGIN_URL)
|
login_url = kwargs.pop('login_url', settings.LOGIN_URL)
|
||||||
redirect_field_name = kwargs.pop("redirect_field_name", REDIRECT_FIELD_NAME)
|
redirect_field_name = kwargs.pop('redirect_field_name', REDIRECT_FIELD_NAME)
|
||||||
redirect_to_login = kwargs.pop("redirect_to_login", True)
|
redirect_to_login = kwargs.pop('redirect_to_login', True)
|
||||||
|
|
||||||
def decorate(view_func):
|
def decorate(view_func):
|
||||||
def decorated(request, *args, **kwargs):
|
def decorated(request, *args, **kwargs):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated():
|
||||||
params = []
|
params = []
|
||||||
for lookup_variable in lookup_variables:
|
for lookup_variable in lookup_variables:
|
||||||
if isinstance(lookup_variable, basestring):
|
if isinstance(lookup_variable, basestring):
|
||||||
|
|
@ -43,19 +34,16 @@ def permission_required(perm, *lookup_variables, **kwargs):
|
||||||
if value is None:
|
if value is None:
|
||||||
continue
|
continue
|
||||||
if isinstance(model, basestring):
|
if isinstance(model, basestring):
|
||||||
model_class = apps.get_model(*model.split("."))
|
model_class = get_model(*model.split("."))
|
||||||
else:
|
else:
|
||||||
model_class = model
|
model_class = model
|
||||||
if model_class is None:
|
if model_class is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"The given argument '%s' is not a valid model." % model
|
"The given argument '%s' is not a valid model." % model)
|
||||||
)
|
if (inspect.isclass(model_class) and
|
||||||
if inspect.isclass(model_class) and not issubclass(
|
not issubclass(model_class, Model)):
|
||||||
model_class, Model
|
|
||||||
):
|
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"The argument %s needs to be a model." % model
|
'The argument %s needs to be a model.' % model)
|
||||||
)
|
|
||||||
obj = get_object_or_404(model_class, **{lookup: value})
|
obj = get_object_or_404(model_class, **{lookup: value})
|
||||||
params.append(obj)
|
params.append(obj)
|
||||||
check = get_check(request.user, perm)
|
check = get_check(request.user, perm)
|
||||||
|
|
@ -67,18 +55,15 @@ def permission_required(perm, *lookup_variables, **kwargs):
|
||||||
if redirect_to_login:
|
if redirect_to_login:
|
||||||
path = urlquote(request.get_full_path())
|
path = urlquote(request.get_full_path())
|
||||||
tup = login_url, redirect_field_name, path
|
tup = login_url, redirect_field_name, path
|
||||||
return HttpResponseRedirect("%s?%s=%s" % tup)
|
return HttpResponseRedirect('%s?%s=%s' % tup)
|
||||||
return permission_denied(request)
|
return permission_denied(request)
|
||||||
|
|
||||||
return wraps(view_func)(decorated)
|
return wraps(view_func)(decorated)
|
||||||
|
|
||||||
return decorate
|
return decorate
|
||||||
|
|
||||||
|
|
||||||
def permission_required_or_403(perm, *args, **kwargs):
|
def permission_required_or_403(perm, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Decorator that wraps the permission_required decorator and returns a
|
Decorator that wraps the permission_required decorator and returns a
|
||||||
permission denied (403) page instead of redirecting to the login URL.
|
permission denied (403) page instead of redirecting to the login URL.
|
||||||
"""
|
"""
|
||||||
kwargs["redirect_to_login"] = False
|
kwargs['redirect_to_login'] = False
|
||||||
return permission_required(perm, *args, **kwargs)
|
return permission_required(perm, *args, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
class AuthorityException(Exception):
|
class AuthorityException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NotAModel(AuthorityException):
|
class NotAModel(AuthorityException):
|
||||||
def __init__(self, object):
|
def __init__(self, object):
|
||||||
super(NotAModel, self).__init__("Not a model class or instance")
|
super(NotAModel, self).__init__(
|
||||||
|
"Not a model class or instance")
|
||||||
|
|
||||||
class UnsavedModelInstance(AuthorityException):
|
class UnsavedModelInstance(AuthorityException):
|
||||||
def __init__(self, object):
|
def __init__(self, object):
|
||||||
super(UnsavedModelInstance, self).__init__(
|
super(UnsavedModelInstance, self).__init__(
|
||||||
"Model instance has no pk, was it saved?"
|
"Model instance has no pk, was it saved?")
|
||||||
)
|
|
||||||
|
|
|
||||||
20
authority/fixtures/tests.json
Normal file
20
authority/fixtures/tests.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"pk": 1,
|
||||||
|
"model": "auth.user",
|
||||||
|
"fields": {
|
||||||
|
"username": "jezdez",
|
||||||
|
"first_name": "",
|
||||||
|
"last_name": "",
|
||||||
|
"is_active": true,
|
||||||
|
"is_superuser": false,
|
||||||
|
"is_staff": false,
|
||||||
|
"last_login": "2009-11-02 03:06:19",
|
||||||
|
"groups": [],
|
||||||
|
"user_permissions": [],
|
||||||
|
"password": "",
|
||||||
|
"email": "",
|
||||||
|
"date_joined": "2009-11-02 03:06:19"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
"model": "users.user",
|
"model": "users.user",
|
||||||
"fields": {
|
"fields": {
|
||||||
"first_name": "Jez",
|
"first_name": "Jez",
|
||||||
"last_name": "Dez",
|
"last_name": "Dez",
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"is_superuser": false,
|
"is_superuser": false,
|
||||||
"is_staff": false,
|
"is_staff": false,
|
||||||
"last_login": "2009-11-02 03:06:19",
|
"last_login": "2009-11-02 03:06:19",
|
||||||
"groups": [],
|
"groups": [],
|
||||||
"user_permissions": [],
|
"user_permissions": [],
|
||||||
"password": "",
|
"password": "",
|
||||||
"email": "jezdez@github.com",
|
"email": "jezdez@github.com",
|
||||||
"date_joined": "2009-11-02 03:06:19",
|
"date_joined": "2009-11-02 03:06:19",
|
||||||
"greeting_message": "Hello customer user model"
|
"greeting_message": "Hello customer user model"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,29 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from authority import permissions
|
from authority import permissions, get_choices_for
|
||||||
from authority.utils import get_choices_for
|
|
||||||
from authority.models import Permission
|
from authority.models import Permission
|
||||||
|
from authority.utils import User
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
class BasePermissionForm(forms.ModelForm):
|
class BasePermissionForm(forms.ModelForm):
|
||||||
codename = forms.CharField(label=_("Permission"))
|
codename = forms.CharField(label=_('Permission'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Permission
|
model = Permission
|
||||||
exclude = []
|
|
||||||
|
|
||||||
def __init__(self, perm=None, obj=None, approved=False, *args, **kwargs):
|
def __init__(self, perm=None, obj=None, approved=False, *args, **kwargs):
|
||||||
self.perm = perm
|
self.perm = perm
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self.approved = approved
|
self.approved = approved
|
||||||
if obj and perm:
|
if obj and perm:
|
||||||
self.base_fields["codename"].widget = forms.HiddenInput()
|
self.base_fields['codename'].widget = forms.HiddenInput()
|
||||||
elif obj and (not perm or not approved):
|
elif obj and (not perm or not approved):
|
||||||
perms = get_choices_for(self.obj)
|
perms = get_choices_for(self.obj)
|
||||||
self.base_fields["codename"].widget = forms.Select(choices=perms)
|
self.base_fields['codename'].widget = forms.Select(choices=perms)
|
||||||
super(BasePermissionForm, self).__init__(*args, **kwargs)
|
super(BasePermissionForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def save(self, request, commit=True, *args, **kwargs):
|
def save(self, request, commit=True, *args, **kwargs):
|
||||||
|
|
@ -38,16 +34,15 @@ class BasePermissionForm(forms.ModelForm):
|
||||||
self.instance.approved = self.approved
|
self.instance.approved = self.approved
|
||||||
return super(BasePermissionForm, self).save(commit)
|
return super(BasePermissionForm, self).save(commit)
|
||||||
|
|
||||||
|
|
||||||
class UserPermissionForm(BasePermissionForm):
|
class UserPermissionForm(BasePermissionForm):
|
||||||
user = forms.CharField(label=_("User"))
|
user = forms.CharField(label=_('User'))
|
||||||
|
|
||||||
class Meta(BasePermissionForm.Meta):
|
class Meta(BasePermissionForm.Meta):
|
||||||
fields = ("user",)
|
fields = ('user',)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if not kwargs.get("approved", False):
|
if not kwargs.get('approved', False):
|
||||||
self.base_fields["user"].widget = forms.HiddenInput()
|
self.base_fields['user'].widget = forms.HiddenInput()
|
||||||
super(UserPermissionForm, self).__init__(*args, **kwargs)
|
super(UserPermissionForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean_user(self):
|
def clean_user(self):
|
||||||
|
|
@ -56,41 +51,34 @@ class UserPermissionForm(BasePermissionForm):
|
||||||
user = User.objects.get(username__iexact=username)
|
user = User.objects.get(username__iexact=username)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
mark_safe(_("A user with that username does not exist."))
|
mark_safe(_("A user with that username does not exist.")))
|
||||||
)
|
|
||||||
check = permissions.BasePermission(user=user)
|
check = permissions.BasePermission(user=user)
|
||||||
error_msg = None
|
error_msg = None
|
||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
error_msg = _(
|
error_msg = _("The user %(user)s do not need to request "
|
||||||
"The user %(user)s do not need to request "
|
"access to any permission as it is a super user.")
|
||||||
"access to any permission as it is a super user."
|
|
||||||
)
|
|
||||||
elif check.has_perm(self.perm, self.obj):
|
elif check.has_perm(self.perm, self.obj):
|
||||||
error_msg = _(
|
error_msg = _("The user %(user)s already has the permission "
|
||||||
"The user %(user)s already has the permission "
|
"'%(perm)s' for %(object_name)s '%(obj)s'")
|
||||||
"'%(perm)s' for %(object_name)s '%(obj)s'"
|
|
||||||
)
|
|
||||||
elif check.requested_perm(self.perm, self.obj):
|
elif check.requested_perm(self.perm, self.obj):
|
||||||
error_msg = _(
|
error_msg = _("The user %(user)s already requested the permission"
|
||||||
"The user %(user)s already requested the permission"
|
" '%(perm)s' for %(object_name)s '%(obj)s'")
|
||||||
" '%(perm)s' for %(object_name)s '%(obj)s'"
|
|
||||||
)
|
|
||||||
if error_msg:
|
if error_msg:
|
||||||
error_msg = error_msg % {
|
error_msg = error_msg % {
|
||||||
"object_name": self.obj._meta.object_name.lower(),
|
'object_name': self.obj._meta.object_name.lower(),
|
||||||
"perm": self.perm,
|
'perm': self.perm,
|
||||||
"obj": self.obj,
|
'obj': self.obj,
|
||||||
"user": user,
|
'user': user,
|
||||||
}
|
}
|
||||||
raise forms.ValidationError(mark_safe(error_msg))
|
raise forms.ValidationError(mark_safe(error_msg))
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
class GroupPermissionForm(BasePermissionForm):
|
class GroupPermissionForm(BasePermissionForm):
|
||||||
group = forms.CharField(label=_("Group"))
|
group = forms.CharField(label=_('Group'))
|
||||||
|
|
||||||
class Meta(BasePermissionForm.Meta):
|
class Meta(BasePermissionForm.Meta):
|
||||||
fields = ("group",)
|
fields = ('group',)
|
||||||
|
|
||||||
def clean_group(self):
|
def clean_group(self):
|
||||||
groupname = self.cleaned_data["group"]
|
groupname = self.cleaned_data["group"]
|
||||||
|
|
@ -98,21 +86,13 @@ class GroupPermissionForm(BasePermissionForm):
|
||||||
group = Group.objects.get(name__iexact=groupname)
|
group = Group.objects.get(name__iexact=groupname)
|
||||||
except Group.DoesNotExist:
|
except Group.DoesNotExist:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
mark_safe(_("A group with that name does not exist."))
|
mark_safe(_("A group with that name does not exist.")))
|
||||||
)
|
|
||||||
check = permissions.BasePermission(group=group)
|
check = permissions.BasePermission(group=group)
|
||||||
if check.has_perm(self.perm, self.obj):
|
if check.has_perm(self.perm, self.obj):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(mark_safe(
|
||||||
mark_safe(
|
_("This group already has the permission '%(perm)s' for %(object_name)s '%(obj)s'") % {
|
||||||
_(
|
'perm': self.perm,
|
||||||
"This group already has the permission '%(perm)s' "
|
'object_name': self.obj._meta.object_name.lower(),
|
||||||
"for %(object_name)s '%(obj)s'"
|
'obj': self.obj,
|
||||||
)
|
}))
|
||||||
% {
|
|
||||||
"perm": self.perm,
|
|
||||||
"object_name": self.obj._meta.object_name.lower(),
|
|
||||||
"obj": self.obj,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return group
|
return group
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
|
||||||
class PermissionManager(models.Manager):
|
class PermissionManager(models.Manager):
|
||||||
|
|
||||||
def get_content_type(self, obj):
|
def get_content_type(self, obj):
|
||||||
return ContentType.objects.get_for_model(obj)
|
return ContentType.objects.get_for_model(obj)
|
||||||
|
|
||||||
|
|
@ -11,41 +12,40 @@ class PermissionManager(models.Manager):
|
||||||
return self.filter(content_type=self.get_content_type(obj))
|
return self.filter(content_type=self.get_content_type(obj))
|
||||||
|
|
||||||
def for_object(self, obj, approved=True):
|
def for_object(self, obj, approved=True):
|
||||||
return (
|
return self.get_for_model(obj).select_related(
|
||||||
self.get_for_model(obj)
|
'user', 'creator', 'group', 'content_type'
|
||||||
.select_related("user", "creator", "group", "content_type")
|
).filter(object_id=obj.id, approved=approved)
|
||||||
.filter(object_id=obj.id, approved=approved)
|
|
||||||
)
|
|
||||||
|
|
||||||
def for_user(self, user, obj, check_groups=True):
|
def for_user(self, user, obj, check_groups=True):
|
||||||
perms = self.get_for_model(obj)
|
perms = self.get_for_model(obj)
|
||||||
if not check_groups:
|
if not check_groups:
|
||||||
return perms.select_related("user", "creator").filter(user=user)
|
return perms.select_related('user', 'creator').filter(user=user)
|
||||||
|
|
||||||
# Hacking user to user__pk to workaround deepcopy bug:
|
# Hacking user to user__pk to workaround deepcopy bug:
|
||||||
# http://bugs.python.org/issue2460
|
# http://bugs.python.org/issue2460
|
||||||
# Which is triggered by django's deepcopy which backports that fix in
|
# Which is triggered by django's deepcopy which backports that fix in
|
||||||
# Django 1.2
|
# Django 1.2
|
||||||
return (
|
return perms.select_related('user', 'user__groups', 'creator').filter(
|
||||||
perms.select_related("user", "creator")
|
Q(user__pk=user.pk) | Q(group__in=user.groups.all()))
|
||||||
.prefetch_related("user__groups")
|
|
||||||
.filter(Q(user__pk=user.pk) | Q(group__in=user.groups.all()))
|
|
||||||
)
|
|
||||||
|
|
||||||
def user_permissions(self, user, perm, obj, approved=True, check_groups=True):
|
def user_permissions(
|
||||||
return self.for_user(user, obj, check_groups,).filter(
|
self, user, perm, obj, approved=True, check_groups=True):
|
||||||
codename=perm, approved=approved,
|
return self.for_user(
|
||||||
|
user,
|
||||||
|
obj,
|
||||||
|
check_groups,
|
||||||
|
).filter(
|
||||||
|
codename=perm,
|
||||||
|
approved=approved,
|
||||||
)
|
)
|
||||||
|
|
||||||
def group_permissions(self, group, perm, obj, approved=True):
|
def group_permissions(self, group, perm, obj, approved=True):
|
||||||
"""
|
"""
|
||||||
Get objects that have Group perm permission on
|
Get objects that have Group perm permission on
|
||||||
"""
|
"""
|
||||||
return (
|
return self.get_for_model(obj).select_related(
|
||||||
self.get_for_model(obj)
|
'user', 'group', 'creator').filter(group=group, codename=perm,
|
||||||
.select_related("user", "group", "creator")
|
approved=approved)
|
||||||
.filter(group=group, codename=perm, approved=approved)
|
|
||||||
)
|
|
||||||
|
|
||||||
def delete_objects_permissions(self, obj):
|
def delete_objects_permissions(self, obj):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
import datetime
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("auth", "0001_initial"),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
("contenttypes", "0001_initial"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="Permission",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.AutoField(
|
|
||||||
verbose_name="ID",
|
|
||||||
serialize=False,
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("codename", models.CharField(max_length=100, verbose_name="codename")),
|
|
||||||
("object_id", models.PositiveIntegerField()),
|
|
||||||
(
|
|
||||||
"approved",
|
|
||||||
models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="Designates whether the permission has been approved and treated as active. Unselect this instead of deleting permissions.",
|
|
||||||
verbose_name="approved",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"date_requested",
|
|
||||||
models.DateTimeField(
|
|
||||||
default=datetime.datetime.now, verbose_name="date requested"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"date_approved",
|
|
||||||
models.DateTimeField(
|
|
||||||
null=True, verbose_name="date approved", blank=True
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"content_type",
|
|
||||||
models.ForeignKey(
|
|
||||||
related_name="row_permissions",
|
|
||||||
to="contenttypes.ContentType",
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"creator",
|
|
||||||
models.ForeignKey(
|
|
||||||
related_name="created_permissions",
|
|
||||||
blank=True,
|
|
||||||
to=settings.AUTH_USER_MODEL,
|
|
||||||
null=True,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"group",
|
|
||||||
models.ForeignKey(
|
|
||||||
blank=True, to="auth.Group", null=True, on_delete=models.CASCADE
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"user",
|
|
||||||
models.ForeignKey(
|
|
||||||
related_name="granted_permissions",
|
|
||||||
blank=True,
|
|
||||||
to=settings.AUTH_USER_MODEL,
|
|
||||||
null=True,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"verbose_name": "permission",
|
|
||||||
"verbose_name_plural": "permissions",
|
|
||||||
"permissions": (
|
|
||||||
("change_foreign_permissions", "Can change foreign permissions"),
|
|
||||||
("delete_foreign_permissions", "Can delete foreign permissions"),
|
|
||||||
("approve_permission_requests", "Can approve permission requests"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
bases=(models.Model,),
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name="permission",
|
|
||||||
unique_together=set(
|
|
||||||
[("codename", "object_id", "content_type", "user", "group")]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
@ -2,13 +2,12 @@ from datetime import datetime
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes import generic
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import User, Group
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from authority.managers import PermissionManager
|
from authority.managers import PermissionManager
|
||||||
|
from authority.utils import User
|
||||||
USER_MODEL = getattr(settings, "AUTH_USER_MODEL", "auth.User")
|
|
||||||
|
|
||||||
|
|
||||||
class Permission(models.Model):
|
class Permission(models.Model):
|
||||||
|
|
@ -17,41 +16,19 @@ class Permission(models.Model):
|
||||||
This kind of permission is associated with a user/group and an object
|
This kind of permission is associated with a user/group and an object
|
||||||
of any content type.
|
of any content type.
|
||||||
"""
|
"""
|
||||||
|
codename = models.CharField(_('codename'), max_length=100)
|
||||||
codename = models.CharField(_("codename"), max_length=100)
|
content_type = models.ForeignKey(ContentType, related_name="row_permissions")
|
||||||
content_type = models.ForeignKey(
|
|
||||||
ContentType, related_name="row_permissions", on_delete=models.CASCADE
|
|
||||||
)
|
|
||||||
object_id = models.PositiveIntegerField()
|
object_id = models.PositiveIntegerField()
|
||||||
content_object = GenericForeignKey("content_type", "object_id")
|
content_object = generic.GenericForeignKey('content_type', 'object_id')
|
||||||
|
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(User, null=True, blank=True, related_name='granted_permissions')
|
||||||
USER_MODEL,
|
group = models.ForeignKey(Group, null=True, blank=True)
|
||||||
null=True,
|
creator = models.ForeignKey(User, null=True, blank=True, related_name='created_permissions')
|
||||||
blank=True,
|
|
||||||
related_name="granted_permissions",
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
group = models.ForeignKey(Group, null=True, blank=True, on_delete=models.CASCADE)
|
|
||||||
creator = models.ForeignKey(
|
|
||||||
USER_MODEL,
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
related_name="created_permissions",
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
|
|
||||||
approved = models.BooleanField(
|
approved = models.BooleanField(_('approved'), default=False, help_text=_("Designates whether the permission has been approved and treated as active. Unselect this instead of deleting permissions."))
|
||||||
_("approved"),
|
|
||||||
default=False,
|
|
||||||
help_text=_(
|
|
||||||
"Designates whether the permission has been approved and treated as active. "
|
|
||||||
"Unselect this instead of deleting permissions."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
date_requested = models.DateTimeField(_("date requested"), default=datetime.now)
|
date_requested = models.DateTimeField(_('date requested'), default=datetime.now)
|
||||||
date_approved = models.DateTimeField(_("date approved"), blank=True, null=True)
|
date_approved = models.DateTimeField(_('date approved'), blank=True, null=True)
|
||||||
|
|
||||||
objects = PermissionManager()
|
objects = PermissionManager()
|
||||||
|
|
||||||
|
|
@ -60,12 +37,12 @@ class Permission(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("codename", "object_id", "content_type", "user", "group")
|
unique_together = ("codename", "object_id", "content_type", "user", "group")
|
||||||
verbose_name = _("permission")
|
verbose_name = _('permission')
|
||||||
verbose_name_plural = _("permissions")
|
verbose_name_plural = _('permissions')
|
||||||
permissions = (
|
permissions = (
|
||||||
("change_foreign_permissions", "Can change foreign permissions"),
|
('change_foreign_permissions', 'Can change foreign permissions'),
|
||||||
("delete_foreign_permissions", "Can delete foreign permissions"),
|
('delete_foreign_permissions', 'Can delete foreign permissions'),
|
||||||
("approve_permission_requests", "Can approve permission requests"),
|
('approve_permission_requests', 'Can approve permission requests'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ class PermissionMetaclass(type):
|
||||||
Used to generate the default set of permission checks "add", "change" and
|
Used to generate the default set of permission checks "add", "change" and
|
||||||
"delete".
|
"delete".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
new_class = super(PermissionMetaclass, cls).__new__(cls, name, bases, attrs)
|
new_class = super(
|
||||||
|
PermissionMetaclass, cls).__new__(cls, name, bases, attrs)
|
||||||
if not new_class.label:
|
if not new_class.label:
|
||||||
new_class.label = "%s_permission" % new_class.__name__.lower()
|
new_class.label = "%s_permission" % new_class.__name__.lower()
|
||||||
new_class.label = slugify(new_class.label)
|
new_class.label = slugify(new_class.label)
|
||||||
|
|
@ -31,12 +31,11 @@ class BasePermission(object):
|
||||||
"""
|
"""
|
||||||
Base Permission class to be used to define app permissions.
|
Base Permission class to be used to define app permissions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__metaclass__ = PermissionMetaclass
|
__metaclass__ = PermissionMetaclass
|
||||||
|
|
||||||
checks = ()
|
checks = ()
|
||||||
label = None
|
label = None
|
||||||
generic_checks = ["add", "browse", "change", "delete"]
|
generic_checks = ['add', 'browse', 'change', 'delete']
|
||||||
|
|
||||||
def __init__(self, user=None, group=None, *args, **kwargs):
|
def __init__(self, user=None, group=None, *args, **kwargs):
|
||||||
self.user = user
|
self.user = user
|
||||||
|
|
@ -49,7 +48,10 @@ class BasePermission(object):
|
||||||
"""
|
"""
|
||||||
if not self.user:
|
if not self.user:
|
||||||
return {}, {}
|
return {}, {}
|
||||||
group_pks = set(self.user.groups.values_list("pk", flat=True,))
|
group_pks = set(self.user.groups.values_list(
|
||||||
|
'pk',
|
||||||
|
flat=True,
|
||||||
|
))
|
||||||
perms = Permission.objects.filter(
|
perms = Permission.objects.filter(
|
||||||
Q(user__pk=self.user.pk) | Q(group__pk__in=group_pks),
|
Q(user__pk=self.user.pk) | Q(group__pk__in=group_pks),
|
||||||
)
|
)
|
||||||
|
|
@ -57,26 +59,22 @@ class BasePermission(object):
|
||||||
group_permissions = {}
|
group_permissions = {}
|
||||||
for perm in perms:
|
for perm in perms:
|
||||||
if perm.user_id == self.user.pk:
|
if perm.user_id == self.user.pk:
|
||||||
user_permissions[
|
user_permissions[(
|
||||||
(
|
perm.object_id,
|
||||||
perm.object_id,
|
perm.content_type_id,
|
||||||
perm.content_type_id,
|
perm.codename,
|
||||||
perm.codename,
|
perm.approved,
|
||||||
perm.approved,
|
)] = True
|
||||||
)
|
|
||||||
] = True
|
|
||||||
# If the user has the permission do for something, but perm.user !=
|
# If the user has the permission do for something, but perm.user !=
|
||||||
# self.user then by definition that permission came from the
|
# self.user then by definition that permission came from the
|
||||||
# group.
|
# group.
|
||||||
else:
|
else:
|
||||||
group_permissions[
|
group_permissions[(
|
||||||
(
|
perm.object_id,
|
||||||
perm.object_id,
|
perm.content_type_id,
|
||||||
perm.content_type_id,
|
perm.codename,
|
||||||
perm.codename,
|
perm.approved,
|
||||||
perm.approved,
|
)] = True
|
||||||
)
|
|
||||||
] = True
|
|
||||||
return user_permissions, group_permissions
|
return user_permissions, group_permissions
|
||||||
|
|
||||||
def _get_group_cached_perms(self):
|
def _get_group_cached_perms(self):
|
||||||
|
|
@ -85,12 +83,17 @@ class BasePermission(object):
|
||||||
"""
|
"""
|
||||||
if not self.group:
|
if not self.group:
|
||||||
return {}
|
return {}
|
||||||
perms = Permission.objects.filter(group=self.group,)
|
perms = Permission.objects.filter(
|
||||||
|
group=self.group,
|
||||||
|
)
|
||||||
group_permissions = {}
|
group_permissions = {}
|
||||||
for perm in perms:
|
for perm in perms:
|
||||||
group_permissions[
|
group_permissions[(
|
||||||
(perm.object_id, perm.content_type_id, perm.codename, perm.approved,)
|
perm.object_id,
|
||||||
] = True
|
perm.content_type_id,
|
||||||
|
perm.codename,
|
||||||
|
perm.approved,
|
||||||
|
)] = True
|
||||||
return group_permissions
|
return group_permissions
|
||||||
|
|
||||||
def _prime_user_perm_caches(self):
|
def _prime_user_perm_caches(self):
|
||||||
|
|
@ -120,7 +123,11 @@ class BasePermission(object):
|
||||||
# Check to see if the cache has been primed.
|
# Check to see if the cache has been primed.
|
||||||
if not self.user:
|
if not self.user:
|
||||||
return {}
|
return {}
|
||||||
cache_filled = getattr(self.user, "_authority_perm_cache_filled", False,)
|
cache_filled = getattr(
|
||||||
|
self.user,
|
||||||
|
'_authority_perm_cache_filled',
|
||||||
|
False,
|
||||||
|
)
|
||||||
if cache_filled:
|
if cache_filled:
|
||||||
# Don't really like the name for this, but this matches how Django
|
# Don't really like the name for this, but this matches how Django
|
||||||
# does it.
|
# does it.
|
||||||
|
|
@ -138,7 +145,11 @@ class BasePermission(object):
|
||||||
# Check to see if the cache has been primed.
|
# Check to see if the cache has been primed.
|
||||||
if not self.group:
|
if not self.group:
|
||||||
return {}
|
return {}
|
||||||
cache_filled = getattr(self.group, "_authority_perm_cache_filled", False,)
|
cache_filled = getattr(
|
||||||
|
self.group,
|
||||||
|
'_authority_perm_cache_filled',
|
||||||
|
False,
|
||||||
|
)
|
||||||
if cache_filled:
|
if cache_filled:
|
||||||
# Don't really like the name for this, but this matches how Django
|
# Don't really like the name for this, but this matches how Django
|
||||||
# does it.
|
# does it.
|
||||||
|
|
@ -156,7 +167,11 @@ class BasePermission(object):
|
||||||
# Check to see if the cache has been primed.
|
# Check to see if the cache has been primed.
|
||||||
if not self.user:
|
if not self.user:
|
||||||
return {}
|
return {}
|
||||||
cache_filled = getattr(self.user, "_authority_perm_cache_filled", False,)
|
cache_filled = getattr(
|
||||||
|
self.user,
|
||||||
|
'_authority_perm_cache_filled',
|
||||||
|
False,
|
||||||
|
)
|
||||||
if cache_filled:
|
if cache_filled:
|
||||||
return self.user._authority_group_perm_cache
|
return self.user._authority_group_perm_cache
|
||||||
|
|
||||||
|
|
@ -179,7 +194,9 @@ class BasePermission(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def use_smart_cache(self):
|
def use_smart_cache(self):
|
||||||
use_smart_cache = getattr(settings, "AUTHORITY_USE_SMART_CACHE", True)
|
# AUTHORITY_USE_SMART_CACHE defaults to False to maintain backwards
|
||||||
|
# compatibility.
|
||||||
|
use_smart_cache = getattr(settings, 'AUTHORITY_USE_SMART_CACHE', True)
|
||||||
return (self.user or self.group) and use_smart_cache
|
return (self.user or self.group) and use_smart_cache
|
||||||
|
|
||||||
def has_user_perms(self, perm, obj, approved, check_groups=True):
|
def has_user_perms(self, perm, obj, approved, check_groups=True):
|
||||||
|
|
@ -195,7 +212,12 @@ class BasePermission(object):
|
||||||
|
|
||||||
def _user_has_perms(cached_perms):
|
def _user_has_perms(cached_perms):
|
||||||
# Check to see if the permission is in the cache.
|
# Check to see if the permission is in the cache.
|
||||||
return cached_perms.get((obj.pk, content_type_pk, perm, approved,))
|
return cached_perms.get((
|
||||||
|
obj.pk,
|
||||||
|
content_type_pk,
|
||||||
|
perm,
|
||||||
|
approved,
|
||||||
|
))
|
||||||
|
|
||||||
# Check to see if the permission is in the cache.
|
# Check to see if the permission is in the cache.
|
||||||
if _user_has_perms(self._user_perm_cache):
|
if _user_has_perms(self._user_perm_cache):
|
||||||
|
|
@ -207,13 +229,15 @@ class BasePermission(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Actually hit the DB, no smart cache used.
|
# Actually hit the DB, no smart cache used.
|
||||||
return (
|
return Permission.objects.user_permissions(
|
||||||
Permission.objects.user_permissions(
|
self.user,
|
||||||
self.user, perm, obj, approved, check_groups,
|
perm,
|
||||||
)
|
obj,
|
||||||
.filter(object_id=obj.pk,)
|
approved,
|
||||||
.exists()
|
check_groups,
|
||||||
)
|
).filter(
|
||||||
|
object_id=obj.pk,
|
||||||
|
).exists()
|
||||||
|
|
||||||
def has_group_perms(self, perm, obj, approved):
|
def has_group_perms(self, perm, obj, approved):
|
||||||
"""
|
"""
|
||||||
|
|
@ -227,17 +251,24 @@ class BasePermission(object):
|
||||||
|
|
||||||
def _group_has_perms(cached_perms):
|
def _group_has_perms(cached_perms):
|
||||||
# Check to see if the permission is in the cache.
|
# Check to see if the permission is in the cache.
|
||||||
return cached_perms.get((obj.pk, content_type_pk, perm, approved,))
|
return cached_perms.get((
|
||||||
|
obj.pk,
|
||||||
|
content_type_pk,
|
||||||
|
perm,
|
||||||
|
approved,
|
||||||
|
))
|
||||||
|
|
||||||
# Check to see if the permission is in the cache.
|
# Check to see if the permission is in the cache.
|
||||||
return _group_has_perms(self._group_perm_cache)
|
return _group_has_perms(self._group_perm_cache)
|
||||||
|
|
||||||
# Actually hit the DB, no smart cache used.
|
# Actually hit the DB, no smart cache used.
|
||||||
return (
|
return Permission.objects.group_permissions(
|
||||||
Permission.objects.group_permissions(self.group, perm, obj, approved,)
|
self.group,
|
||||||
.filter(object_id=obj.pk,)
|
perm, obj,
|
||||||
.exists()
|
approved,
|
||||||
)
|
).filter(
|
||||||
|
object_id=obj.pk,
|
||||||
|
).exists()
|
||||||
|
|
||||||
def has_perm(self, perm, obj, check_groups=True, approved=True):
|
def has_perm(self, perm, obj, check_groups=True, approved=True):
|
||||||
"""
|
"""
|
||||||
|
|
@ -276,20 +307,25 @@ class BasePermission(object):
|
||||||
return perms
|
return perms
|
||||||
|
|
||||||
def get_django_codename(
|
def get_django_codename(
|
||||||
self, check, model_or_instance, generic=False, without_left=False
|
self, check, model_or_instance, generic=False, without_left=False):
|
||||||
):
|
|
||||||
if without_left:
|
if without_left:
|
||||||
perm = check
|
perm = check
|
||||||
else:
|
else:
|
||||||
perm = "%s.%s" % (model_or_instance._meta.app_label, check.lower())
|
perm = '%s.%s' % (model_or_instance._meta.app_label, check.lower())
|
||||||
if generic:
|
if generic:
|
||||||
perm = "%s_%s" % (perm, model_or_instance._meta.object_name.lower(),)
|
perm = '%s_%s' % (
|
||||||
|
perm,
|
||||||
|
model_or_instance._meta.object_name.lower(),
|
||||||
|
)
|
||||||
return perm
|
return perm
|
||||||
|
|
||||||
def get_codename(self, check, model_or_instance, generic=False):
|
def get_codename(self, check, model_or_instance, generic=False):
|
||||||
perm = "%s.%s" % (self.label, check.lower())
|
perm = '%s.%s' % (self.label, check.lower())
|
||||||
if generic:
|
if generic:
|
||||||
perm = "%s_%s" % (perm, model_or_instance._meta.object_name.lower(),)
|
perm = '%s_%s' % (
|
||||||
|
perm,
|
||||||
|
model_or_instance._meta.object_name.lower(),
|
||||||
|
)
|
||||||
return perm
|
return perm
|
||||||
|
|
||||||
def assign(self, check=None, content_object=None, generic=False):
|
def assign(self, check=None, content_object=None, generic=False):
|
||||||
|
|
@ -311,7 +347,7 @@ class BasePermission(object):
|
||||||
content_objects = content_object
|
content_objects = content_object
|
||||||
|
|
||||||
if not check:
|
if not check:
|
||||||
checks = self.generic_checks + getattr(self, "checks", [])
|
checks = self.generic_checks + getattr(self, 'checks', [])
|
||||||
elif not isinstance(check, (list, tuple)):
|
elif not isinstance(check, (list, tuple)):
|
||||||
checks = (check,)
|
checks = (check,)
|
||||||
else:
|
else:
|
||||||
|
|
@ -330,11 +366,14 @@ class BasePermission(object):
|
||||||
for check in checks:
|
for check in checks:
|
||||||
if isinstance(content_object, Model):
|
if isinstance(content_object, Model):
|
||||||
# make an authority per object permission
|
# make an authority per object permission
|
||||||
codename = self.get_codename(check, content_object, generic,)
|
codename = self.get_codename(
|
||||||
|
check,
|
||||||
|
content_object,
|
||||||
|
generic,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
perm = Permission.objects.get(
|
perm = Permission.objects.get(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
group=self.group,
|
|
||||||
codename=codename,
|
codename=codename,
|
||||||
approved=True,
|
approved=True,
|
||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
|
|
@ -343,7 +382,6 @@ class BasePermission(object):
|
||||||
except Permission.DoesNotExist:
|
except Permission.DoesNotExist:
|
||||||
perm = Permission.objects.create(
|
perm = Permission.objects.create(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
group=self.group,
|
|
||||||
content_object=content_object,
|
content_object=content_object,
|
||||||
codename=codename,
|
codename=codename,
|
||||||
approved=True,
|
approved=True,
|
||||||
|
|
@ -354,16 +392,21 @@ class BasePermission(object):
|
||||||
elif isinstance(content_object, ModelBase):
|
elif isinstance(content_object, ModelBase):
|
||||||
# make a Django permission
|
# make a Django permission
|
||||||
codename = self.get_django_codename(
|
codename = self.get_django_codename(
|
||||||
check, content_object, generic, without_left=True,
|
check,
|
||||||
|
content_object,
|
||||||
|
generic,
|
||||||
|
without_left=True,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
perm = DjangoPermission.objects.get(codename=codename)
|
perm = DjangoPermission.objects.get(codename=codename)
|
||||||
except DjangoPermission.DoesNotExist:
|
except DjangoPermission.DoesNotExist:
|
||||||
name = check
|
name = check
|
||||||
if "_" in name:
|
if '_' in name:
|
||||||
name = name[0 : name.find("_")]
|
name = name[0:name.find('_')]
|
||||||
perm = DjangoPermission(
|
perm = DjangoPermission(
|
||||||
name=name, codename=codename, content_type=content_type,
|
name=name,
|
||||||
|
codename=codename,
|
||||||
|
content_type=content_type,
|
||||||
)
|
)
|
||||||
perm.save()
|
perm.save()
|
||||||
self.user.user_permissions.add(perm)
|
self.user.user_permissions.add(perm)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from inspect import getmembers, ismethod
|
from inspect import getmembers, ismethod
|
||||||
from django.apps import apps
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.base import ModelBase
|
from django.db.models.base import ModelBase
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
@ -7,20 +6,16 @@ from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from authority.permissions import BasePermission
|
from authority.permissions import BasePermission
|
||||||
|
|
||||||
|
|
||||||
class AlreadyRegistered(Exception):
|
class AlreadyRegistered(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NotRegistered(Exception):
|
class NotRegistered(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PermissionSite(object):
|
class PermissionSite(object):
|
||||||
"""
|
"""
|
||||||
A dictionary that contains permission instances and their labels.
|
A dictionary that contains permission instances and their labels.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_registry = {}
|
_registry = {}
|
||||||
_choices = {}
|
_choices = {}
|
||||||
|
|
||||||
|
|
@ -34,7 +29,7 @@ class PermissionSite(object):
|
||||||
return [perm for perm in self._registry.values() if perm.model == model]
|
return [perm for perm in self._registry.values() if perm.model == model]
|
||||||
|
|
||||||
def get_check(self, user, label):
|
def get_check(self, user, label):
|
||||||
perm_label, check_name = label.split(".")
|
perm_label, check_name = label.split('.')
|
||||||
perm_cls = self.get_permission_by_label(perm_label)
|
perm_cls = self.get_permission_by_label(perm_label)
|
||||||
if perm_cls is None:
|
if perm_cls is None:
|
||||||
return None
|
return None
|
||||||
|
|
@ -54,8 +49,8 @@ class PermissionSite(object):
|
||||||
for perm in self.get_permissions_by_model(model_cls):
|
for perm in self.get_permissions_by_model(model_cls):
|
||||||
for name, check in getmembers(perm, ismethod):
|
for name, check in getmembers(perm, ismethod):
|
||||||
if name in perm.checks:
|
if name in perm.checks:
|
||||||
signature = "%s.%s" % (perm.label, name)
|
signature = '%s.%s' % (perm.label, name)
|
||||||
label = getattr(check, "short_description", signature)
|
label = getattr(check, 'short_description', signature)
|
||||||
choices.append((signature, label))
|
choices.append((signature, label))
|
||||||
self._choices[model_cls] = choices
|
self._choices[model_cls] = choices
|
||||||
return choices
|
return choices
|
||||||
|
|
@ -69,23 +64,17 @@ class PermissionSite(object):
|
||||||
|
|
||||||
if permission_class.label in self.get_labels():
|
if permission_class.label in self.get_labels():
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"The name of %s conflicts with %s"
|
"The name of %s conflicts with %s" % (permission_class,
|
||||||
% (
|
self.get_permission_by_label(permission_class.label)))
|
||||||
permission_class,
|
|
||||||
self.get_permission_by_label(permission_class.label),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
for model in model_or_iterable:
|
for model in model_or_iterable:
|
||||||
if model in self._registry:
|
if model in self._registry:
|
||||||
raise AlreadyRegistered(
|
raise AlreadyRegistered(
|
||||||
"The model %s is already registered" % model.__name__
|
'The model %s is already registered' % model.__name__)
|
||||||
)
|
|
||||||
if options:
|
if options:
|
||||||
options["__module__"] = __name__
|
options['__module__'] = __name__
|
||||||
permission_class = type(
|
permission_class = type("%sPermission" % model.__name__,
|
||||||
"%sPermission" % model.__name__, (permission_class,), options
|
(permission_class,), options)
|
||||||
)
|
|
||||||
|
|
||||||
permission_class.model = model
|
permission_class.model = model
|
||||||
self.setup(model, permission_class)
|
self.setup(model, permission_class)
|
||||||
|
|
@ -96,7 +85,7 @@ class PermissionSite(object):
|
||||||
model_or_iterable = [model_or_iterable]
|
model_or_iterable = [model_or_iterable]
|
||||||
for model in model_or_iterable:
|
for model in model_or_iterable:
|
||||||
if model not in self._registry:
|
if model not in self._registry:
|
||||||
raise NotRegistered("The model %s is not registered" % model.__name__)
|
raise NotRegistered('The model %s is not registered' % model.__name__)
|
||||||
del self._registry[model]
|
del self._registry[model]
|
||||||
|
|
||||||
def setup(self, model, permission):
|
def setup(self, model, permission):
|
||||||
|
|
@ -105,12 +94,10 @@ class PermissionSite(object):
|
||||||
if check_func is not None:
|
if check_func is not None:
|
||||||
func = self.create_check(check_name, check_func)
|
func = self.create_check(check_name, check_func)
|
||||||
func.__name__ = check_name
|
func.__name__ = check_name
|
||||||
func.short_description = getattr(
|
func.short_description = getattr(check_func, 'short_description',
|
||||||
check_func,
|
_("%(object_name)s permission '%(check)s'") % {
|
||||||
"short_description",
|
'object_name': model._meta.object_name,
|
||||||
_("%(object_name)s permission '%(check)s'")
|
'check': check_name})
|
||||||
% {"object_name": model._meta.object_name, "check": check_name},
|
|
||||||
)
|
|
||||||
setattr(permission, check_name, func)
|
setattr(permission, check_name, func)
|
||||||
else:
|
else:
|
||||||
permission.generic_checks.append(check_name)
|
permission.generic_checks.append(check_name)
|
||||||
|
|
@ -119,12 +106,11 @@ class PermissionSite(object):
|
||||||
object_name = model._meta.object_name
|
object_name = model._meta.object_name
|
||||||
func_name = "%s_%s" % (check_name, object_name.lower())
|
func_name = "%s_%s" % (check_name, object_name.lower())
|
||||||
func.short_description = _("Can %(check)s this %(object_name)s") % {
|
func.short_description = _("Can %(check)s this %(object_name)s") % {
|
||||||
"object_name": model._meta.object_name.lower(),
|
'object_name': model._meta.object_name.lower(),
|
||||||
"check": check_name,
|
'check': check_name}
|
||||||
}
|
|
||||||
func.check_name = check_name
|
func.check_name = check_name
|
||||||
if func_name not in permission.checks:
|
if func_name not in permission.checks:
|
||||||
permission.checks = list(permission.checks) + [func_name]
|
permission.checks = (list(permission.checks) + [func_name])
|
||||||
setattr(permission, func_name, func)
|
setattr(permission, func_name, func)
|
||||||
setattr(model, "permissions", PermissionDescriptor())
|
setattr(model, "permissions", PermissionDescriptor())
|
||||||
|
|
||||||
|
|
@ -134,19 +120,15 @@ class PermissionSite(object):
|
||||||
if check_func and not granted:
|
if check_func and not granted:
|
||||||
return check_func(self, *args, **kwargs)
|
return check_func(self, *args, **kwargs)
|
||||||
return granted
|
return granted
|
||||||
|
|
||||||
return check
|
return check
|
||||||
|
|
||||||
|
|
||||||
class PermissionDescriptor(object):
|
class PermissionDescriptor(object):
|
||||||
def get_content_type(self, obj=None):
|
def get_content_type(self, obj=None):
|
||||||
ContentType = apps.get_model("contenttypes", "contenttype")
|
ContentType = models.get_model("contenttypes", "contenttype")
|
||||||
if obj:
|
if obj:
|
||||||
return ContentType.objects.get_for_model(obj)
|
return ContentType.objects.get_for_model(obj)
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception("Invalid arguments given to PermissionDescriptor.get_content_type")
|
||||||
"Invalid arguments given to PermissionDescriptor.get_content_type"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
|
|
@ -154,7 +136,6 @@ class PermissionDescriptor(object):
|
||||||
ct = self.get_content_type(instance)
|
ct = self.get_content_type(instance)
|
||||||
return ct.row_permissions.all()
|
return ct.row_permissions.all()
|
||||||
|
|
||||||
|
|
||||||
site = PermissionSite()
|
site = PermissionSite()
|
||||||
get_check = site.get_check
|
get_check = site.get_check
|
||||||
get_choices_for = site.get_choices_for
|
get_choices_for = site.get_choices_for
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
{% if inline_admin_form.form.non_field_errors %}
|
{% if inline_admin_form.form.non_field_errors %}
|
||||||
<tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
|
<tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr class="{% cycle 'row1' 'row2' %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}">
|
<tr class="{% cycle row1,row2 %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}">
|
||||||
|
|
||||||
<td class="original">
|
<td class="original">
|
||||||
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,20 @@
|
||||||
{% load i18n admin_modify admin_static %}
|
{% load i18n admin_modify admin_static %}
|
||||||
|
|
||||||
{% block extrahead %}{{ block.super }}
|
{% block extrahead %}{{ block.super }}
|
||||||
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
|
<script type="text/javascript" src="../../../jsi18n/"></script>
|
||||||
{{ media }}
|
{{ media }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}"/>{% endblock %}
|
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}"/>{% endblock %}
|
||||||
|
|
||||||
{% block coltype %}colM{% endblock %}
|
{% block coltype %}{% if ordered_objects %}colMS{% else %}colM{% endif %}{% endblock %}
|
||||||
|
|
||||||
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
|
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
|
||||||
|
|
||||||
{% block breadcrumbs %}{% if not is_popup %}
|
{% block breadcrumbs %}{% if not is_popup %}
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="../../">{% trans "Home" %}</a> ›
|
<a href="../../">{% trans "Home" %}</a> ›
|
||||||
<a href="../">{{ app_label|capfirst|escape }}</a> ›
|
<a href="../">{{ app_label|capfirst|escape }}</a> ›
|
||||||
<a href="./">{{ opts.verbose_name_plural|capfirst }}</a> ›
|
<a href="./">{{ opts.verbose_name_plural|capfirst }}</a> ›
|
||||||
{% trans "Permissions" %}
|
{% trans "Permissions" %}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
{% block content %}<div id="content-main">
|
{% block content %}<div id="content-main">
|
||||||
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">
|
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">
|
||||||
{% csrf_token %}
|
|
||||||
<div>
|
<div>
|
||||||
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
|
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
|
||||||
{% if save_on_top %}{% submit_row %}{% endif %}
|
{% if save_on_top %}{% submit_row %}{% endif %}
|
||||||
|
|
@ -45,7 +44,7 @@
|
||||||
{% block after_related_objects %}{% endblock %}
|
{% block after_related_objects %}{% endblock %}
|
||||||
|
|
||||||
<div class="submit-row">
|
<div class="submit-row">
|
||||||
<input type="submit" value="{% trans 'Save' %}" class="default" name="_save_action" {{ onclick_attrib }}/>
|
<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" {{ onclick_attrib }}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,39 @@
|
||||||
from django import template
|
from django import template
|
||||||
from django.contrib.auth import get_user_model
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.urls import reverse
|
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from authority.utils import get_check
|
from authority import get_check
|
||||||
from authority import permissions
|
from authority import permissions
|
||||||
from authority.models import Permission
|
from authority.models import Permission
|
||||||
from authority.forms import UserPermissionForm
|
from authority.forms import UserPermissionForm
|
||||||
|
from authority.utils import User
|
||||||
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def url_for_obj(view_name, obj):
|
def url_for_obj(view_name, obj):
|
||||||
return reverse(
|
return reverse(view_name, kwargs={
|
||||||
view_name,
|
'app_label': obj._meta.app_label,
|
||||||
kwargs={
|
'module_name': obj._meta.module_name,
|
||||||
"app_label": obj._meta.app_label,
|
'pk': obj.pk})
|
||||||
"module_name": obj._meta.module_name,
|
|
||||||
"pk": obj.pk,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def add_url_for_obj(obj):
|
def add_url_for_obj(obj):
|
||||||
return url_for_obj("authority-add-permission", obj)
|
return url_for_obj('authority-add-permission', obj)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def request_url_for_obj(obj):
|
def request_url_for_obj(obj):
|
||||||
return url_for_obj("authority-add-permission-request", obj)
|
return url_for_obj('authority-add-permission-request', obj)
|
||||||
|
|
||||||
|
|
||||||
class ResolverNode(template.Node):
|
class ResolverNode(template.Node):
|
||||||
"""
|
"""
|
||||||
A small wrapper that adds a convenient resolve method.
|
A small wrapper that adds a convenient resolve method.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def resolve(self, var, context):
|
def resolve(self, var, context):
|
||||||
"""Resolves a variable out of context if it's not in quotes"""
|
"""Resolves a variable out of context if it's not in quotes"""
|
||||||
if var is None:
|
if var is None:
|
||||||
|
|
@ -53,7 +46,7 @@ class ResolverNode(template.Node):
|
||||||
@classmethod
|
@classmethod
|
||||||
def next_bit_for(cls, bits, key, if_none=None):
|
def next_bit_for(cls, bits, key, if_none=None):
|
||||||
try:
|
try:
|
||||||
return bits[bits.index(key) + 1]
|
return bits[bits.index(key)+1]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return if_none
|
return if_none
|
||||||
|
|
||||||
|
|
@ -62,27 +55,25 @@ class PermissionComparisonNode(ResolverNode):
|
||||||
"""
|
"""
|
||||||
Implements a node to provide an "if user/group has permission on object"
|
Implements a node to provide an "if user/group has permission on object"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_token(cls, parser, token):
|
def handle_token(cls, parser, token):
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
if 5 < len(bits) < 3:
|
if 5 < len(bits) < 3:
|
||||||
raise template.TemplateSyntaxError(
|
raise template.TemplateSyntaxError("'%s' tag takes three, "
|
||||||
"'%s' tag takes three, " "four or five arguments" % bits[0]
|
"four or five arguments" % bits[0])
|
||||||
)
|
end_tag = 'endifhasperm'
|
||||||
end_tag = "endifhasperm"
|
nodelist_true = parser.parse(('else', end_tag))
|
||||||
nodelist_true = parser.parse(("else", end_tag))
|
|
||||||
token = parser.next_token()
|
token = parser.next_token()
|
||||||
if token.contents == "else": # there is an 'else' clause in the tag
|
if token.contents == 'else': # there is an 'else' clause in the tag
|
||||||
nodelist_false = parser.parse((end_tag,))
|
nodelist_false = parser.parse((end_tag,))
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
else:
|
else:
|
||||||
nodelist_false = template.NodeList()
|
nodelist_false = template.NodeList()
|
||||||
if len(bits) == 3: # this tag requires at most 2 objects . None is given
|
if len(bits) == 3: # this tag requires at most 2 objects . None is given
|
||||||
objs = (None, None)
|
objs = (None, None)
|
||||||
elif len(bits) == 4: # one is given
|
elif len(bits) == 4:# one is given
|
||||||
objs = (bits[3], None)
|
objs = (bits[3], None)
|
||||||
else: # two are given
|
else: #two are given
|
||||||
objs = (bits[3], bits[4])
|
objs = (bits[3], bits[4])
|
||||||
return cls(bits[2], bits[1], nodelist_true, nodelist_false, *objs)
|
return cls(bits[2], bits[1], nodelist_true, nodelist_false, *objs)
|
||||||
|
|
||||||
|
|
@ -111,16 +102,15 @@ class PermissionComparisonNode(ResolverNode):
|
||||||
return self.nodelist_true.render(context)
|
return self.nodelist_true.render(context)
|
||||||
# If the app couldn't be found
|
# If the app couldn't be found
|
||||||
except (ImproperlyConfigured, ImportError):
|
except (ImproperlyConfigured, ImportError):
|
||||||
return ""
|
return ''
|
||||||
# If either variable fails to resolve, return nothing.
|
# If either variable fails to resolve, return nothing.
|
||||||
except template.VariableDoesNotExist:
|
except template.VariableDoesNotExist:
|
||||||
return ""
|
return ''
|
||||||
# If the types don't permit comparison, return nothing.
|
# If the types don't permit comparison, return nothing.
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError):
|
||||||
return ""
|
return ''
|
||||||
return self.nodelist_false.render(context)
|
return self.nodelist_false.render(context)
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def ifhasperm(parser, token):
|
def ifhasperm(parser, token):
|
||||||
"""
|
"""
|
||||||
|
|
@ -134,7 +124,7 @@ def ifhasperm(parser, token):
|
||||||
meh
|
meh
|
||||||
{% endifhasperm %}
|
{% endifhasperm %}
|
||||||
|
|
||||||
{% ifhasperm "poll_permission.change_poll" request.user %}
|
{% if hasperm "poll_permission.change_poll" request.user %}
|
||||||
lalala
|
lalala
|
||||||
{% else %}
|
{% else %}
|
||||||
meh
|
meh
|
||||||
|
|
@ -145,14 +135,16 @@ def ifhasperm(parser, token):
|
||||||
|
|
||||||
|
|
||||||
class PermissionFormNode(ResolverNode):
|
class PermissionFormNode(ResolverNode):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_token(cls, parser, token, approved):
|
def handle_token(cls, parser, token, approved):
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
|
tag_name = bits[0]
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"obj": cls.next_bit_for(bits, "for"),
|
'obj': cls.next_bit_for(bits, 'for'),
|
||||||
"perm": cls.next_bit_for(bits, "using", None),
|
'perm': cls.next_bit_for(bits, 'using', None),
|
||||||
"template_name": cls.next_bit_for(bits, "with", ""),
|
'template_name': cls.next_bit_for(bits, 'with', ''),
|
||||||
"approved": approved,
|
'approved': approved,
|
||||||
}
|
}
|
||||||
return cls(**kwargs)
|
return cls(**kwargs)
|
||||||
|
|
||||||
|
|
@ -166,40 +158,33 @@ class PermissionFormNode(ResolverNode):
|
||||||
obj = self.resolve(self.obj, context)
|
obj = self.resolve(self.obj, context)
|
||||||
perm = self.resolve(self.perm, context)
|
perm = self.resolve(self.perm, context)
|
||||||
if self.template_name:
|
if self.template_name:
|
||||||
template_name = [
|
template_name = [self.resolve(obj, context) for obj in self.template_name.split(',')]
|
||||||
self.resolve(o, context) for o in self.template_name.split(",")
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
template_name = "authority/permission_form.html"
|
template_name = 'authority/permission_form.html'
|
||||||
request = context["request"]
|
request = context['request']
|
||||||
extra_context = {}
|
extra_context = {}
|
||||||
if self.approved:
|
if self.approved:
|
||||||
if request.user.is_authenticated and request.user.has_perm(
|
if (request.user.is_authenticated() and
|
||||||
"authority.add_permission"
|
request.user.has_perm('authority.add_permission')):
|
||||||
):
|
|
||||||
extra_context = {
|
extra_context = {
|
||||||
"form_url": url_for_obj("authority-add-permission", obj),
|
'form_url': url_for_obj('authority-add-permission', obj),
|
||||||
"next": request.build_absolute_uri(),
|
'next': request.build_absolute_uri(),
|
||||||
"approved": self.approved,
|
'approved': self.approved,
|
||||||
"form": UserPermissionForm(
|
'form': UserPermissionForm(perm, obj, approved=self.approved,
|
||||||
perm, obj, approved=self.approved, initial=dict(codename=perm)
|
initial=dict(codename=perm)),
|
||||||
),
|
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
if request.user.is_authenticated and not request.user.is_superuser:
|
if request.user.is_authenticated() and not request.user.is_superuser:
|
||||||
extra_context = {
|
extra_context = {
|
||||||
"form_url": url_for_obj("authority-add-permission-request", obj),
|
'form_url': url_for_obj('authority-add-permission-request', obj),
|
||||||
"next": request.build_absolute_uri(),
|
'next': request.build_absolute_uri(),
|
||||||
"approved": self.approved,
|
'approved': self.approved,
|
||||||
"form": UserPermissionForm(
|
'form': UserPermissionForm(perm, obj,
|
||||||
perm,
|
approved=self.approved, initial=dict(
|
||||||
obj,
|
codename=perm, user=request.user.username)),
|
||||||
approved=self.approved,
|
|
||||||
initial=dict(codename=perm, user=request.user.username),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
return template.loader.render_to_string(template_name, extra_context, request)
|
return template.loader.render_to_string(template_name, extra_context,
|
||||||
|
context_instance=template.RequestContext(request))
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def permission_form(parser, token):
|
def permission_form(parser, token):
|
||||||
|
|
@ -215,7 +200,6 @@ def permission_form(parser, token):
|
||||||
"""
|
"""
|
||||||
return PermissionFormNode.handle_token(parser, token, approved=True)
|
return PermissionFormNode.handle_token(parser, token, approved=True)
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def permission_request_form(parser, token):
|
def permission_request_form(parser, token):
|
||||||
"""
|
"""
|
||||||
|
|
@ -225,23 +209,23 @@ def permission_request_form(parser, token):
|
||||||
Syntax::
|
Syntax::
|
||||||
|
|
||||||
{% permission_request_form for OBJ and PERMISSION_LABEL.CHECK_NAME [with TEMPLATE] %}
|
{% permission_request_form for OBJ and PERMISSION_LABEL.CHECK_NAME [with TEMPLATE] %}
|
||||||
{% permission_request_form for lesson using "lesson_permission.add_lesson"
|
{% permission_request_form for lesson using "lesson_permission.add_lesson" with "authority/permission_request_form.html" %}
|
||||||
with "authority/permission_request_form.html" %}
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return PermissionFormNode.handle_token(parser, token, approved=False)
|
return PermissionFormNode.handle_token(parser, token, approved=False)
|
||||||
|
|
||||||
|
|
||||||
class PermissionsForObjectNode(ResolverNode):
|
class PermissionsForObjectNode(ResolverNode):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_token(cls, parser, token, approved, name):
|
def handle_token(cls, parser, token, approved, name):
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
tag_name = bits[0]
|
tag_name = bits[0]
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"obj": cls.next_bit_for(bits, tag_name),
|
'obj': cls.next_bit_for(bits, tag_name),
|
||||||
"user": cls.next_bit_for(bits, "for"),
|
'user': cls.next_bit_for(bits, 'for'),
|
||||||
"var_name": cls.next_bit_for(bits, "as", name),
|
'var_name': cls.next_bit_for(bits, 'as', name),
|
||||||
"approved": approved,
|
'approved': approved,
|
||||||
}
|
}
|
||||||
return cls(**kwargs)
|
return cls(**kwargs)
|
||||||
|
|
||||||
|
|
@ -262,15 +246,14 @@ class PermissionsForObjectNode(ResolverNode):
|
||||||
if isinstance(user, User):
|
if isinstance(user, User):
|
||||||
perms = perms.filter(user=user)
|
perms = perms.filter(user=user)
|
||||||
context[var_name] = perms
|
context[var_name] = perms
|
||||||
return ""
|
return ''
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def get_permissions(parser, token):
|
def get_permissions(parser, token):
|
||||||
"""
|
"""
|
||||||
Retrieves all permissions associated with the given obj and user
|
Retrieves all permissions associated with the given obj and user
|
||||||
and assigns the result to a context variable.
|
and assigns the result to a context variable.
|
||||||
|
|
||||||
Syntax::
|
Syntax::
|
||||||
|
|
||||||
{% get_permissions obj %}
|
{% get_permissions obj %}
|
||||||
|
|
@ -282,17 +265,15 @@ def get_permissions(parser, token):
|
||||||
{% get_permissions obj for request.user as "my_permissions" %}
|
{% get_permissions obj for request.user as "my_permissions" %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return PermissionsForObjectNode.handle_token(
|
return PermissionsForObjectNode.handle_token(parser, token, approved=True,
|
||||||
parser, token, approved=True, name='"permissions"'
|
name='"permissions"')
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def get_permission_requests(parser, token):
|
def get_permission_requests(parser, token):
|
||||||
"""
|
"""
|
||||||
Retrieves all permissions requests associated with the given obj and user
|
Retrieves all permissions requests associated with the given obj and user
|
||||||
and assigns the result to a context variable.
|
and assigns the result to a context variable.
|
||||||
|
|
||||||
Syntax::
|
Syntax::
|
||||||
|
|
||||||
{% get_permission_requests obj %}
|
{% get_permission_requests obj %}
|
||||||
|
|
@ -304,22 +285,22 @@ def get_permission_requests(parser, token):
|
||||||
{% get_permission_requests obj for request.user as "my_permissions" %}
|
{% get_permission_requests obj for request.user as "my_permissions" %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return PermissionsForObjectNode.handle_token(
|
return PermissionsForObjectNode.handle_token(parser, token,
|
||||||
parser, token, approved=False, name='"permission_requests"'
|
approved=False,
|
||||||
)
|
name='"permission_requests"')
|
||||||
|
|
||||||
|
|
||||||
class PermissionForObjectNode(ResolverNode):
|
class PermissionForObjectNode(ResolverNode):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_token(cls, parser, token, approved, name):
|
def handle_token(cls, parser, token, approved, name):
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
tag_name = bits[0]
|
tag_name = bits[0]
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"perm": cls.next_bit_for(bits, tag_name),
|
'perm': cls.next_bit_for(bits, tag_name),
|
||||||
"user": cls.next_bit_for(bits, "for"),
|
'user': cls.next_bit_for(bits, 'for'),
|
||||||
"objs": cls.next_bit_for(bits, "and"),
|
'objs': cls.next_bit_for(bits, 'and'),
|
||||||
"var_name": cls.next_bit_for(bits, "as", name),
|
'var_name': cls.next_bit_for(bits, 'as', name),
|
||||||
"approved": approved,
|
'approved': approved,
|
||||||
}
|
}
|
||||||
return cls(**kwargs)
|
return cls(**kwargs)
|
||||||
|
|
||||||
|
|
@ -331,7 +312,7 @@ class PermissionForObjectNode(ResolverNode):
|
||||||
self.approved = approved
|
self.approved = approved
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
objs = [self.resolve(obj, context) for obj in self.objs.split(",")]
|
objs = [self.resolve(obj, context) for obj in self.objs.split(',')]
|
||||||
var_name = self.resolve(self.var_name, context)
|
var_name = self.resolve(self.var_name, context)
|
||||||
perm = self.resolve(self.perm, context)
|
perm = self.resolve(self.perm, context)
|
||||||
user = self.resolve(self.user, context)
|
user = self.resolve(self.user, context)
|
||||||
|
|
@ -348,8 +329,7 @@ class PermissionForObjectNode(ResolverNode):
|
||||||
if granted:
|
if granted:
|
||||||
break
|
break
|
||||||
context[var_name] = granted
|
context[var_name] = granted
|
||||||
return ""
|
return ''
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def get_permission(parser, token):
|
def get_permission(parser, token):
|
||||||
|
|
@ -361,11 +341,9 @@ def get_permission(parser, token):
|
||||||
|
|
||||||
{% get_permission PERMISSION_LABEL.CHECK_NAME for USER and *OBJS [as VARNAME] %}
|
{% get_permission PERMISSION_LABEL.CHECK_NAME for USER and *OBJS [as VARNAME] %}
|
||||||
|
|
||||||
{% get_permission "poll_permission.change_poll"
|
{% get_permission "poll_permission.change_poll" for request.user and poll as "is_allowed" %}
|
||||||
for request.user and poll as "is_allowed" %}
|
{% get_permission "poll_permission.change_poll" for request.user and poll,second_poll as "is_allowed" %}
|
||||||
{% get_permission "poll_permission.change_poll"
|
|
||||||
for request.user and poll,second_poll as "is_allowed" %}
|
|
||||||
|
|
||||||
{% if is_allowed %}
|
{% if is_allowed %}
|
||||||
I've got ze power to change ze pollllllzzz. Muahahaa.
|
I've got ze power to change ze pollllllzzz. Muahahaa.
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
@ -373,10 +351,9 @@ def get_permission(parser, token):
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return PermissionForObjectNode.handle_token(
|
return PermissionForObjectNode.handle_token(parser, token,
|
||||||
parser, token, approved=True, name='"permission"'
|
approved=True,
|
||||||
)
|
name='"permission"')
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def get_permission_request(parser, token):
|
def get_permission_request(parser, token):
|
||||||
|
|
@ -388,11 +365,9 @@ def get_permission_request(parser, token):
|
||||||
|
|
||||||
{% get_permission_request PERMISSION_LABEL.CHECK_NAME for USER and *OBJS [as VARNAME] %}
|
{% get_permission_request PERMISSION_LABEL.CHECK_NAME for USER and *OBJS [as VARNAME] %}
|
||||||
|
|
||||||
{% get_permission_request "poll_permission.change_poll"
|
{% get_permission_request "poll_permission.change_poll" for request.user and poll as "asked_for_permissio" %}
|
||||||
for request.user and poll as "asked_for_permissio" %}
|
{% get_permission_request "poll_permission.change_poll" for request.user and poll,second_poll as "asked_for_permissio" %}
|
||||||
{% get_permission_request "poll_permission.change_poll"
|
|
||||||
for request.user and poll,second_poll as "asked_for_permissio" %}
|
|
||||||
|
|
||||||
{% if asked_for_permissio %}
|
{% if asked_for_permissio %}
|
||||||
Dude, you already asked for permission!
|
Dude, you already asked for permission!
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
@ -400,67 +375,59 @@ def get_permission_request(parser, token):
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return PermissionForObjectNode.handle_token(
|
return PermissionForObjectNode.handle_token(parser, token,
|
||||||
parser, token, approved=False, name='"permission_request"'
|
approved=False,
|
||||||
)
|
name='"permission_request"')
|
||||||
|
|
||||||
|
|
||||||
def base_link(context, perm, view_name):
|
def base_link(context, perm, view_name):
|
||||||
return {
|
return {
|
||||||
"next": context["request"].build_absolute_uri(),
|
'next': context['request'].build_absolute_uri(),
|
||||||
"url": reverse(view_name, kwargs={"permission_pk": perm.pk}),
|
'url': reverse(view_name, kwargs={'permission_pk': perm.pk,}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@register.inclusion_tag('authority/permission_delete_link.html', takes_context=True)
|
||||||
@register.inclusion_tag("authority/permission_delete_link.html", takes_context=True)
|
|
||||||
def permission_delete_link(context, perm):
|
def permission_delete_link(context, perm):
|
||||||
"""
|
"""
|
||||||
Renders a html link to the delete view of the given permission. Returns
|
Renders a html link to the delete view of the given permission. Returns
|
||||||
no content if the request-user has no permission to delete foreign
|
no content if the request-user has no permission to delete foreign
|
||||||
permissions.
|
permissions.
|
||||||
"""
|
"""
|
||||||
user = context["request"].user
|
user = context['request'].user
|
||||||
if user.is_authenticated:
|
if user.is_authenticated():
|
||||||
if (
|
if user.has_perm('authority.delete_foreign_permissions') \
|
||||||
user.has_perm("authority.delete_foreign_permissions")
|
or user.pk == perm.creator.pk:
|
||||||
or user.pk == perm.creator.pk
|
return base_link(context, perm, 'authority-delete-permission')
|
||||||
):
|
return {'url': None}
|
||||||
return base_link(context, perm, "authority-delete-permission")
|
|
||||||
return {"url": None}
|
|
||||||
|
|
||||||
|
@register.inclusion_tag('authority/permission_request_delete_link.html', takes_context=True)
|
||||||
@register.inclusion_tag(
|
|
||||||
"authority/permission_request_delete_link.html", takes_context=True
|
|
||||||
)
|
|
||||||
def permission_request_delete_link(context, perm):
|
def permission_request_delete_link(context, perm):
|
||||||
"""
|
"""
|
||||||
Renders a html link to the delete view of the given permission request.
|
Renders a html link to the delete view of the given permission request.
|
||||||
Returns no content if the request-user has no permission to delete foreign
|
Returns no content if the request-user has no permission to delete foreign
|
||||||
permissions.
|
permissions.
|
||||||
"""
|
"""
|
||||||
user = context["request"].user
|
user = context['request'].user
|
||||||
if user.is_authenticated:
|
if user.is_authenticated():
|
||||||
link_kwargs = base_link(context, perm, "authority-delete-permission-request")
|
link_kwargs = base_link(context, perm,
|
||||||
if user.has_perm("authority.delete_permission"):
|
'authority-delete-permission-request')
|
||||||
link_kwargs["is_requestor"] = False
|
if user.has_perm('authority.delete_permission'):
|
||||||
|
link_kwargs['is_requestor'] = False
|
||||||
return link_kwargs
|
return link_kwargs
|
||||||
if not perm.approved and perm.user == user:
|
if not perm.approved and perm.user == user:
|
||||||
link_kwargs["is_requestor"] = True
|
link_kwargs['is_requestor'] = True
|
||||||
return link_kwargs
|
return link_kwargs
|
||||||
return {"url": None}
|
return {'url': None}
|
||||||
|
|
||||||
|
@register.inclusion_tag('authority/permission_request_approve_link.html', takes_context=True)
|
||||||
@register.inclusion_tag(
|
|
||||||
"authority/permission_request_approve_link.html", takes_context=True
|
|
||||||
)
|
|
||||||
def permission_request_approve_link(context, perm):
|
def permission_request_approve_link(context, perm):
|
||||||
"""
|
"""
|
||||||
Renders a html link to the approve view of the given permission request.
|
Renders a html link to the approve view of the given permission request.
|
||||||
Returns no content if the request-user has no permission to delete foreign
|
Returns no content if the request-user has no permission to delete foreign
|
||||||
permissions.
|
permissions.
|
||||||
"""
|
"""
|
||||||
user = context["request"].user
|
user = context['request'].user
|
||||||
if user.is_authenticated:
|
if user.is_authenticated():
|
||||||
if user.has_perm("authority.approve_permission_requests"):
|
if user.has_perm('authority.approve_permission_requests'):
|
||||||
return base_link(context, perm, "authority-approve-permission-request")
|
return base_link(context, perm,
|
||||||
return {"url": None}
|
'authority-approve-permission-request')
|
||||||
|
return {'url': None}
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,36 @@
|
||||||
|
from django import VERSION
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth.models import Permission as DjangoPermission
|
||||||
from django.contrib.auth.models import Group, Permission as DjangoPermission
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.core.exceptions import MultipleObjectsReturned
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
import authority
|
import authority
|
||||||
from authority import permissions
|
from authority import permissions
|
||||||
from authority.models import Permission
|
from authority.models import Permission
|
||||||
from authority.exceptions import NotAModel, UnsavedModelInstance
|
from authority.exceptions import NotAModel, UnsavedModelInstance
|
||||||
|
from authority.utils import User
|
||||||
# Load the form
|
|
||||||
from authority.forms import UserPermissionForm # noqa
|
|
||||||
|
|
||||||
|
|
||||||
User = get_user_model()
|
if VERSION >= (1, 5):
|
||||||
FIXTURES = ["tests_custom.json"]
|
FIXTURES = ['tests_custom.json']
|
||||||
QUERY = Q(email="jezdez@github.com")
|
QUERY = Q(email="jezdez@github.com")
|
||||||
|
else:
|
||||||
|
FIXTURES = ['tests.json']
|
||||||
|
QUERY = Q(username="jezdez")
|
||||||
|
|
||||||
|
|
||||||
class UserPermission(permissions.BasePermission):
|
class UserPermission(permissions.BasePermission):
|
||||||
checks = ("browse",)
|
checks = ('browse',)
|
||||||
label = "user_permission"
|
label = 'user_permission'
|
||||||
|
authority.register(User, UserPermission)
|
||||||
|
|
||||||
authority.utils.register(User, UserPermission)
|
|
||||||
|
|
||||||
|
|
||||||
class GroupPermission(permissions.BasePermission):
|
class GroupPermission(permissions.BasePermission):
|
||||||
checks = ("browse",)
|
checks = ('browse',)
|
||||||
label = "group_permission"
|
label = 'group_permission'
|
||||||
|
authority.register(Group, GroupPermission)
|
||||||
|
|
||||||
authority.utils.register(Group, GroupPermission)
|
|
||||||
|
|
||||||
|
|
||||||
class DjangoPermissionChecksTestCase(TestCase):
|
class DjangoPermissionChecksTestCase(TestCase):
|
||||||
|
|
@ -49,7 +45,6 @@ class DjangoPermissionChecksTestCase(TestCase):
|
||||||
This permissions are given in the test case and not in the fixture, for
|
This permissions are given in the test case and not in the fixture, for
|
||||||
later reference.
|
later reference.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fixtures = FIXTURES
|
fixtures = FIXTURES
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
@ -63,7 +58,7 @@ class DjangoPermissionChecksTestCase(TestCase):
|
||||||
|
|
||||||
def test_add(self):
|
def test_add(self):
|
||||||
# setup
|
# setup
|
||||||
perm = DjangoPermission.objects.get(codename="add_user")
|
perm = DjangoPermission.objects.get(codename='add_user')
|
||||||
self.user.user_permissions.add(perm)
|
self.user.user_permissions.add(perm)
|
||||||
|
|
||||||
# test
|
# test
|
||||||
|
|
@ -73,8 +68,8 @@ class DjangoPermissionChecksTestCase(TestCase):
|
||||||
perm = Permission(
|
perm = Permission(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
content_object=self.user,
|
content_object=self.user,
|
||||||
codename="user_permission.delete_user",
|
codename='user_permission.delete_user',
|
||||||
approved=True,
|
approved=True
|
||||||
)
|
)
|
||||||
perm.save()
|
perm.save()
|
||||||
|
|
||||||
|
|
@ -90,60 +85,23 @@ class AssignBehaviourTest(TestCase):
|
||||||
- permission delete_user for him (test_delete),
|
- permission delete_user for him (test_delete),
|
||||||
- all existing codenames permissions: a/b/c/d (test_all),
|
- all existing codenames permissions: a/b/c/d (test_all),
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fixtures = FIXTURES
|
fixtures = FIXTURES
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.user = User.objects.get(QUERY)
|
self.user = User.objects.get(QUERY)
|
||||||
self.group1, _ = Group.objects.get_or_create(name="Test Group 1")
|
|
||||||
self.group2, _ = Group.objects.get_or_create(name="Test Group 2")
|
|
||||||
self.group3, _ = Group.objects.get_or_create(name="Test Group 2")
|
|
||||||
self.check = UserPermission(self.user)
|
self.check = UserPermission(self.user)
|
||||||
|
|
||||||
def test_add(self):
|
def test_add(self):
|
||||||
result = self.check.assign(check="add_user")
|
result = self.check.assign(check='add_user')
|
||||||
|
|
||||||
self.assertTrue(isinstance(result[0], DjangoPermission))
|
self.assertTrue(isinstance(result[0], DjangoPermission))
|
||||||
self.assertTrue(self.check.add_user())
|
self.assertTrue(self.check.add_user())
|
||||||
|
|
||||||
def test_assign_to_group(self):
|
|
||||||
result = UserPermission(group=self.group1).assign(
|
|
||||||
check="delete_user", content_object=self.user
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIsInstance(result, list)
|
|
||||||
self.assertIsInstance(result[0], Permission)
|
|
||||||
self.assertTrue(UserPermission(group=self.group1).delete_user(self.user))
|
|
||||||
|
|
||||||
def test_assign_to_group_does_not_overwrite_other_group_permission(self):
|
|
||||||
UserPermission(group=self.group1).assign(
|
|
||||||
check="delete_user", content_object=self.user
|
|
||||||
)
|
|
||||||
UserPermission(group=self.group2).assign(
|
|
||||||
check="delete_user", content_object=self.user
|
|
||||||
)
|
|
||||||
self.assertTrue(UserPermission(group=self.group2).delete_user(self.user))
|
|
||||||
self.assertTrue(UserPermission(group=self.group1).delete_user(self.user))
|
|
||||||
|
|
||||||
def test_assign_to_group_does_not_fail_when_two_group_perms_exist(self):
|
|
||||||
for group in self.group1, self.group2:
|
|
||||||
perm = Permission(
|
|
||||||
group=group,
|
|
||||||
content_object=self.user,
|
|
||||||
codename="user_permission.delete_user",
|
|
||||||
approved=True,
|
|
||||||
)
|
|
||||||
perm.save()
|
|
||||||
|
|
||||||
try:
|
|
||||||
UserPermission(group=self.group3).assign(
|
|
||||||
check="delete_user", content_object=self.user
|
|
||||||
)
|
|
||||||
except MultipleObjectsReturned:
|
|
||||||
self.fail("assign() should not have raised this exception")
|
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
result = self.check.assign(content_object=self.user, check="delete_user",)
|
result = self.check.assign(
|
||||||
|
content_object=self.user,
|
||||||
|
check='delete_user',
|
||||||
|
)
|
||||||
|
|
||||||
self.assertTrue(isinstance(result[0], Permission))
|
self.assertTrue(isinstance(result[0], Permission))
|
||||||
self.assertFalse(self.check.delete_user())
|
self.assertFalse(self.check.delete_user())
|
||||||
|
|
@ -164,7 +122,6 @@ class GenericAssignBehaviourTest(TestCase):
|
||||||
- permission add (test_add),
|
- permission add (test_add),
|
||||||
- permission delete for him (test_delete),
|
- permission delete for him (test_delete),
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fixtures = FIXTURES
|
fixtures = FIXTURES
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
@ -172,14 +129,16 @@ class GenericAssignBehaviourTest(TestCase):
|
||||||
self.check = UserPermission(self.user)
|
self.check = UserPermission(self.user)
|
||||||
|
|
||||||
def test_add(self):
|
def test_add(self):
|
||||||
result = self.check.assign(check="add", generic=True)
|
result = self.check.assign(check='add', generic=True)
|
||||||
|
|
||||||
self.assertTrue(isinstance(result[0], DjangoPermission))
|
self.assertTrue(isinstance(result[0], DjangoPermission))
|
||||||
self.assertTrue(self.check.add_user())
|
self.assertTrue(self.check.add_user())
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
result = self.check.assign(
|
result = self.check.assign(
|
||||||
content_object=self.user, check="delete", generic=True,
|
content_object=self.user,
|
||||||
|
check='delete',
|
||||||
|
generic=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertTrue(isinstance(result[0], Permission))
|
self.assertTrue(isinstance(result[0], Permission))
|
||||||
|
|
@ -192,7 +151,6 @@ class AssignExceptionsTest(TestCase):
|
||||||
Tests that exceptions are thrown if assign() was called with inconsistent
|
Tests that exceptions are thrown if assign() was called with inconsistent
|
||||||
arguments.
|
arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fixtures = FIXTURES
|
fixtures = FIXTURES
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
@ -208,7 +166,7 @@ class AssignExceptionsTest(TestCase):
|
||||||
|
|
||||||
def test_not_model_content_object(self):
|
def test_not_model_content_object(self):
|
||||||
try:
|
try:
|
||||||
self.check.assign(content_object="fail")
|
self.check.assign(content_object='fail')
|
||||||
except NotAModel:
|
except NotAModel:
|
||||||
return True
|
return True
|
||||||
self.fail()
|
self.fail()
|
||||||
|
|
@ -218,7 +176,6 @@ class SmartCachingTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
The base test case for all tests that have to do with smart caching.
|
The base test case for all tests that have to do with smart caching.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fixtures = FIXTURES
|
fixtures = FIXTURES
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
@ -243,14 +200,21 @@ class SmartCachingTestCase(TestCase):
|
||||||
# This is what the old, pre-cache system would check to see if a user
|
# This is what the old, pre-cache system would check to see if a user
|
||||||
# had a given permission.
|
# had a given permission.
|
||||||
return Permission.objects.user_permissions(
|
return Permission.objects.user_permissions(
|
||||||
self.user, "foo", self.user, approved=True, check_groups=True,
|
self.user,
|
||||||
|
'foo',
|
||||||
|
self.user,
|
||||||
|
approved=True,
|
||||||
|
check_groups=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _old_group_permission_check(self):
|
def _old_group_permission_check(self):
|
||||||
# This is what the old, pre-cache system would check to see if a user
|
# This is what the old, pre-cache system would check to see if a user
|
||||||
# had a given permission.
|
# had a given permission.
|
||||||
return Permission.objects.group_permissions(
|
return Permission.objects.group_permissions(
|
||||||
self.group, "foo", self.group, approved=True,
|
self.group,
|
||||||
|
'foo',
|
||||||
|
self.group,
|
||||||
|
approved=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -275,13 +239,20 @@ class PerformanceTest(SmartCachingTestCase):
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
# Need to assert it so the query actually gets executed.
|
# Need to assert it so the query actually gets executed.
|
||||||
assert not self.user_check.has_user_perms(
|
assert not self.user_check.has_user_perms(
|
||||||
"foo", self.user, True, False,
|
'foo',
|
||||||
|
self.user,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_group_has_perms(self):
|
def test_group_has_perms(self):
|
||||||
with self.assertNumQueries(2):
|
with self.assertNumQueries(2):
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
assert not self.group_check.has_group_perms("foo", self.group, True,)
|
assert not self.group_check.has_group_perms(
|
||||||
|
'foo',
|
||||||
|
self.group,
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
def test_has_user_perms_check_group(self):
|
def test_has_user_perms_check_group(self):
|
||||||
# Regardless of the number groups permissions, it should only take one
|
# Regardless of the number groups permissions, it should only take one
|
||||||
|
|
@ -289,7 +260,10 @@ class PerformanceTest(SmartCachingTestCase):
|
||||||
# Content type and permissions (2 queries)
|
# Content type and permissions (2 queries)
|
||||||
with self.assertNumQueries(3):
|
with self.assertNumQueries(3):
|
||||||
self.user_check.has_user_perms(
|
self.user_check.has_user_perms(
|
||||||
"foo", self.user, approved=True, check_groups=True,
|
'foo',
|
||||||
|
self.user,
|
||||||
|
approved=True,
|
||||||
|
check_groups=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalidate_user_permissions_cache(self):
|
def test_invalidate_user_permissions_cache(self):
|
||||||
|
|
@ -301,7 +275,10 @@ class PerformanceTest(SmartCachingTestCase):
|
||||||
with self.assertNumQueries(6):
|
with self.assertNumQueries(6):
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
assert not self.user_check.has_user_perms(
|
assert not self.user_check.has_user_perms(
|
||||||
"foo", self.user, True, False,
|
'foo',
|
||||||
|
self.user,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Invalidate the cache to show that a query will be generated when
|
# Invalidate the cache to show that a query will be generated when
|
||||||
|
|
@ -312,7 +289,10 @@ class PerformanceTest(SmartCachingTestCase):
|
||||||
# One query to re generate the cache.
|
# One query to re generate the cache.
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
assert not self.user_check.has_user_perms(
|
assert not self.user_check.has_user_perms(
|
||||||
"foo", self.user, True, False,
|
'foo',
|
||||||
|
self.user,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalidate_group_permissions_cache(self):
|
def test_invalidate_group_permissions_cache(self):
|
||||||
|
|
@ -322,7 +302,11 @@ class PerformanceTest(SmartCachingTestCase):
|
||||||
# will need to do one query to get content type and one to get
|
# will need to do one query to get content type and one to get
|
||||||
with self.assertNumQueries(4):
|
with self.assertNumQueries(4):
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
assert not self.group_check.has_group_perms("foo", self.group, True,)
|
assert not self.group_check.has_group_perms(
|
||||||
|
'foo',
|
||||||
|
self.group,
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
# Invalidate the cache to show that a query will be generated when
|
# Invalidate the cache to show that a query will be generated when
|
||||||
# checking perms again.
|
# checking perms again.
|
||||||
|
|
@ -331,14 +315,18 @@ class PerformanceTest(SmartCachingTestCase):
|
||||||
|
|
||||||
# One query to re generate the cache.
|
# One query to re generate the cache.
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
assert not self.group_check.has_group_perms("foo", self.group, True,)
|
assert not self.group_check.has_group_perms(
|
||||||
|
'foo',
|
||||||
|
self.group,
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
def test_has_user_perms_check_group_multiple(self):
|
def test_has_user_perms_check_group_multiple(self):
|
||||||
# Create a permission with just a group.
|
# Create a permission with just a group.
|
||||||
Permission.objects.create(
|
Permission.objects.create(
|
||||||
content_type=Permission.objects.get_content_type(User),
|
content_type=Permission.objects.get_content_type(User),
|
||||||
object_id=self.user.pk,
|
object_id=self.user.pk,
|
||||||
codename="foo",
|
codename='foo',
|
||||||
group=self.group,
|
group=self.group,
|
||||||
approved=True,
|
approved=True,
|
||||||
)
|
)
|
||||||
|
|
@ -347,17 +335,17 @@ class PerformanceTest(SmartCachingTestCase):
|
||||||
|
|
||||||
# Check the number of queries.
|
# Check the number of queries.
|
||||||
with self.assertNumQueries(2):
|
with self.assertNumQueries(2):
|
||||||
assert self.user_check.has_user_perms("foo", self.user, True, True)
|
assert self.user_check.has_user_perms('foo', self.user, True, True)
|
||||||
|
|
||||||
# Create a second group.
|
# Create a second group.
|
||||||
new_group = Group.objects.create(name="new_group")
|
new_group = Group.objects.create(name='new_group')
|
||||||
new_group.user_set.add(self.user)
|
new_group.user_set.add(self.user)
|
||||||
|
|
||||||
# Create a permission object for it.
|
# Create a permission object for it.
|
||||||
Permission.objects.create(
|
Permission.objects.create(
|
||||||
content_type=Permission.objects.get_content_type(User),
|
content_type=Permission.objects.get_content_type(User),
|
||||||
object_id=self.user.pk,
|
object_id=self.user.pk,
|
||||||
codename="foo",
|
codename='foo',
|
||||||
group=new_group,
|
group=new_group,
|
||||||
approved=True,
|
approved=True,
|
||||||
)
|
)
|
||||||
|
|
@ -366,7 +354,7 @@ class PerformanceTest(SmartCachingTestCase):
|
||||||
|
|
||||||
# Make sure it is the same number of queries.
|
# Make sure it is the same number of queries.
|
||||||
with self.assertNumQueries(2):
|
with self.assertNumQueries(2):
|
||||||
assert self.user_check.has_user_perms("foo", self.user, True, True)
|
assert self.user_check.has_user_perms('foo', self.user, True, True)
|
||||||
|
|
||||||
|
|
||||||
class GroupPermissionCacheTestCase(SmartCachingTestCase):
|
class GroupPermissionCacheTestCase(SmartCachingTestCase):
|
||||||
|
|
@ -381,7 +369,10 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
|
||||||
# Use the new cached user perms to show that the user does not have the
|
# Use the new cached user perms to show that the user does not have the
|
||||||
# perms.
|
# perms.
|
||||||
can_foo_with_group = self.user_check.has_user_perms(
|
can_foo_with_group = self.user_check.has_user_perms(
|
||||||
"foo", self.user, approved=True, check_groups=True,
|
'foo',
|
||||||
|
self.user,
|
||||||
|
approved=True,
|
||||||
|
check_groups=True,
|
||||||
)
|
)
|
||||||
self.assertFalse(can_foo_with_group)
|
self.assertFalse(can_foo_with_group)
|
||||||
|
|
||||||
|
|
@ -389,7 +380,7 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
|
||||||
perm = Permission.objects.create(
|
perm = Permission.objects.create(
|
||||||
content_type=Permission.objects.get_content_type(User),
|
content_type=Permission.objects.get_content_type(User),
|
||||||
object_id=self.user.pk,
|
object_id=self.user.pk,
|
||||||
codename="foo",
|
codename='foo',
|
||||||
group=self.group,
|
group=self.group,
|
||||||
approved=True,
|
approved=True,
|
||||||
)
|
)
|
||||||
|
|
@ -401,7 +392,10 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
|
||||||
# Invalidate the cache.
|
# Invalidate the cache.
|
||||||
self.user_check.invalidate_permissions_cache()
|
self.user_check.invalidate_permissions_cache()
|
||||||
can_foo_with_group = self.user_check.has_user_perms(
|
can_foo_with_group = self.user_check.has_user_perms(
|
||||||
"foo", self.user, approved=True, check_groups=True,
|
'foo',
|
||||||
|
self.user,
|
||||||
|
approved=True,
|
||||||
|
check_groups=True,
|
||||||
)
|
)
|
||||||
self.assertTrue(can_foo_with_group)
|
self.assertTrue(can_foo_with_group)
|
||||||
|
|
||||||
|
|
@ -409,7 +403,9 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
|
||||||
# Make sure calling has_user_perms on a permission that does not have a
|
# Make sure calling has_user_perms on a permission that does not have a
|
||||||
# user does not throw any errors.
|
# user does not throw any errors.
|
||||||
can_foo_with_group = self.group_check.has_group_perms(
|
can_foo_with_group = self.group_check.has_group_perms(
|
||||||
"foo", self.user, approved=True,
|
'foo',
|
||||||
|
self.user,
|
||||||
|
approved=True,
|
||||||
)
|
)
|
||||||
self.assertFalse(can_foo_with_group)
|
self.assertFalse(can_foo_with_group)
|
||||||
|
|
||||||
|
|
@ -420,7 +416,7 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
|
||||||
perm = Permission.objects.create(
|
perm = Permission.objects.create(
|
||||||
content_type=Permission.objects.get_content_type(Group),
|
content_type=Permission.objects.get_content_type(Group),
|
||||||
object_id=self.group.pk,
|
object_id=self.group.pk,
|
||||||
codename="foo",
|
codename='foo',
|
||||||
group=self.group,
|
group=self.group,
|
||||||
approved=True,
|
approved=True,
|
||||||
)
|
)
|
||||||
|
|
@ -433,21 +429,8 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
|
||||||
self.group_check.invalidate_permissions_cache()
|
self.group_check.invalidate_permissions_cache()
|
||||||
|
|
||||||
can_foo_with_group = self.group_check.has_group_perms(
|
can_foo_with_group = self.group_check.has_group_perms(
|
||||||
"foo", self.group, approved=True,
|
'foo',
|
||||||
|
self.group,
|
||||||
|
approved=True,
|
||||||
)
|
)
|
||||||
self.assertTrue(can_foo_with_group)
|
self.assertTrue(can_foo_with_group)
|
||||||
|
|
||||||
|
|
||||||
class AddPermissionTestCase(TestCase):
|
|
||||||
def test_add_permission_permission_denied_is_403(self):
|
|
||||||
user = get_user_model().objects.create(username="foo", email="foo@example.com",)
|
|
||||||
user.set_password("pw")
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
assert self.client.login(username="foo@example.com", password="pw")
|
|
||||||
url = reverse(
|
|
||||||
"authority-add-permission-request",
|
|
||||||
kwargs={"app_label": "foo", "module_name": "Bar", "pk": 1,},
|
|
||||||
)
|
|
||||||
r = self.client.get(url)
|
|
||||||
self.assertEqual(r.status_code, 403)
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,31 @@
|
||||||
from django.conf.urls import url
|
try:
|
||||||
from authority.views import (
|
from django.conf.urls import *
|
||||||
add_permission,
|
except ImportError: # django < 1.4
|
||||||
delete_permission,
|
from django.conf.urls.defaults import *
|
||||||
approve_permission_request,
|
|
||||||
delete_permission,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
urlpatterns = patterns('authority.views',
|
||||||
urlpatterns = [
|
url(r'^permission/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$',
|
||||||
url(
|
view='add_permission',
|
||||||
r"^permission/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$",
|
|
||||||
view=add_permission,
|
|
||||||
name="authority-add-permission",
|
name="authority-add-permission",
|
||||||
kwargs={"approved": True},
|
kwargs={'approved': True}
|
||||||
),
|
),
|
||||||
url(
|
url(r'^permission/delete/(?P<permission_pk>\d+)/$',
|
||||||
r"^permission/delete/(?P<permission_pk>\d+)/$",
|
view='delete_permission',
|
||||||
view=delete_permission,
|
|
||||||
name="authority-delete-permission",
|
name="authority-delete-permission",
|
||||||
kwargs={"approved": True},
|
kwargs={'approved': True}
|
||||||
),
|
),
|
||||||
url(
|
url(r'^request/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$',
|
||||||
r"^request/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$",
|
view='add_permission',
|
||||||
view=add_permission,
|
|
||||||
name="authority-add-permission-request",
|
name="authority-add-permission-request",
|
||||||
kwargs={"approved": False},
|
kwargs={'approved': False}
|
||||||
),
|
),
|
||||||
url(
|
url(r'^request/approve/(?P<permission_pk>\d+)/$',
|
||||||
r"^request/approve/(?P<permission_pk>\d+)/$",
|
view='approve_permission_request',
|
||||||
view=approve_permission_request,
|
name="authority-approve-permission-request"
|
||||||
name="authority-approve-permission-request",
|
|
||||||
),
|
),
|
||||||
url(
|
url(r'^request/delete/(?P<permission_pk>\d+)/$',
|
||||||
r"^request/delete/(?P<permission_pk>\d+)/$",
|
view='delete_permission',
|
||||||
view=delete_permission,
|
|
||||||
name="authority-delete-permission-request",
|
name="authority-delete-permission-request",
|
||||||
kwargs={"approved": False},
|
kwargs={'approved': False}
|
||||||
),
|
),
|
||||||
]
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
from authority.sites import (
|
from django.contrib import auth
|
||||||
site,
|
|
||||||
get_check,
|
|
||||||
get_choices_for,
|
def get_user_class():
|
||||||
register,
|
if hasattr(auth, "get_user_model"):
|
||||||
unregister,
|
return auth.get_user_model()
|
||||||
) # noqa
|
else:
|
||||||
|
return auth.models.User
|
||||||
|
|
||||||
|
|
||||||
|
User = get_user_class()
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
from django.shortcuts import render, get_object_or_404
|
from datetime import datetime
|
||||||
|
from django.shortcuts import render_to_response, get_object_or_404
|
||||||
|
from django.views.decorators.http import require_POST
|
||||||
from django.http import HttpResponseRedirect, HttpResponseForbidden
|
from django.http import HttpResponseRedirect, HttpResponseForbidden
|
||||||
from django.apps import apps
|
from django.db.models.loading import get_model
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.template.context import RequestContext
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
|
@ -9,108 +12,85 @@ from authority.models import Permission
|
||||||
from authority.forms import UserPermissionForm
|
from authority.forms import UserPermissionForm
|
||||||
from authority.templatetags.permissions import url_for_obj
|
from authority.templatetags.permissions import url_for_obj
|
||||||
|
|
||||||
|
|
||||||
def get_next(request, obj=None):
|
def get_next(request, obj=None):
|
||||||
next = request.REQUEST.get("next")
|
next = request.REQUEST.get('next')
|
||||||
if not next:
|
if not next:
|
||||||
if obj and hasattr(obj, "get_absolute_url"):
|
if obj and hasattr(obj, 'get_absolute_url'):
|
||||||
next = obj.get_absolute_url()
|
next = obj.get_absolute_url()
|
||||||
else:
|
else:
|
||||||
next = "/"
|
next = '/'
|
||||||
return next
|
return next
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def add_permission(
|
def add_permission(request, app_label, module_name, pk, approved=False,
|
||||||
request,
|
template_name = 'authority/permission_form.html',
|
||||||
app_label,
|
extra_context={}, form_class=UserPermissionForm):
|
||||||
module_name,
|
codename = request.POST.get('codename', None)
|
||||||
pk,
|
model = get_model(app_label, module_name)
|
||||||
approved=False,
|
if model is None:
|
||||||
template_name="authority/permission_form.html",
|
|
||||||
extra_context=None,
|
|
||||||
form_class=UserPermissionForm,
|
|
||||||
):
|
|
||||||
codename = request.POST.get("codename", None)
|
|
||||||
try:
|
|
||||||
model = apps.get_model(app_label, module_name)
|
|
||||||
except LookupError:
|
|
||||||
return permission_denied(request)
|
return permission_denied(request)
|
||||||
obj = get_object_or_404(model, pk=pk)
|
obj = get_object_or_404(model, pk=pk)
|
||||||
next = get_next(request, obj)
|
next = get_next(request, obj)
|
||||||
if approved:
|
if approved:
|
||||||
if not request.user.has_perm("authority.add_permission"):
|
if not request.user.has_perm('authority.add_permission'):
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
url_for_obj("authority-add-permission-request", obj)
|
url_for_obj('authority-add-permission-request', obj))
|
||||||
)
|
view_name = 'authority-add-permission'
|
||||||
view_name = "authority-add-permission"
|
|
||||||
else:
|
else:
|
||||||
view_name = "authority-add-permission-request"
|
view_name = 'authority-add-permission-request'
|
||||||
if request.method == "POST":
|
if request.method == 'POST':
|
||||||
if codename is None:
|
if codename is None:
|
||||||
return HttpResponseForbidden(next)
|
return HttpResponseForbidden(next)
|
||||||
form = form_class(
|
form = form_class(data=request.POST, obj=obj, approved=approved,
|
||||||
data=request.POST,
|
perm=codename, initial=dict(codename=codename))
|
||||||
obj=obj,
|
|
||||||
approved=approved,
|
|
||||||
perm=codename,
|
|
||||||
initial=dict(codename=codename),
|
|
||||||
)
|
|
||||||
if not approved:
|
if not approved:
|
||||||
# Limit permission request to current user
|
# Limit permission request to current user
|
||||||
form.data["user"] = request.user
|
form.data['user'] = request.user
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save(request)
|
permission = form.save(request)
|
||||||
request.user.message_set.create(
|
request.user.message_set.create(
|
||||||
message=_("You added a permission request.")
|
message=_('You added a permission request.'))
|
||||||
)
|
|
||||||
return HttpResponseRedirect(next)
|
return HttpResponseRedirect(next)
|
||||||
else:
|
else:
|
||||||
form = form_class(
|
form = form_class(obj=obj, approved=approved, perm=codename,
|
||||||
obj=obj, approved=approved, perm=codename, initial=dict(codename=codename)
|
initial=dict(codename=codename))
|
||||||
)
|
|
||||||
context = {
|
context = {
|
||||||
"form": form,
|
'form': form,
|
||||||
"form_url": url_for_obj(view_name, obj),
|
'form_url': url_for_obj(view_name, obj),
|
||||||
"next": next,
|
'next': next,
|
||||||
"perm": codename,
|
'perm': codename,
|
||||||
"approved": approved,
|
'approved': approved,
|
||||||
}
|
}
|
||||||
if extra_context:
|
context.update(extra_context)
|
||||||
context.update(extra_context)
|
return render_to_response(template_name, context,
|
||||||
return render(request, template_name, context)
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def approve_permission_request(request, permission_pk):
|
def approve_permission_request(request, permission_pk):
|
||||||
requested_permission = get_object_or_404(Permission, pk=permission_pk)
|
requested_permission = get_object_or_404(Permission, pk=permission_pk)
|
||||||
if request.user.has_perm("authority.approve_permission_requests"):
|
if request.user.has_perm('authority.approve_permission_requests'):
|
||||||
requested_permission.approve(request.user)
|
requested_permission.approve(request.user)
|
||||||
request.user.message_set.create(
|
request.user.message_set.create(
|
||||||
message=_("You approved the permission request.")
|
message=_('You approved the permission request.'))
|
||||||
)
|
|
||||||
next = get_next(request, requested_permission)
|
next = get_next(request, requested_permission)
|
||||||
return HttpResponseRedirect(next)
|
return HttpResponseRedirect(next)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_permission(request, permission_pk, approved):
|
def delete_permission(request, permission_pk, approved):
|
||||||
permission = get_object_or_404(Permission, pk=permission_pk, approved=approved)
|
permission = get_object_or_404(Permission, pk=permission_pk,
|
||||||
if (
|
approved=approved)
|
||||||
request.user.has_perm("authority.delete_foreign_permissions")
|
if (request.user.has_perm('authority.delete_foreign_permissions')
|
||||||
or request.user == permission.creator
|
or request.user == permission.creator):
|
||||||
):
|
|
||||||
permission.delete()
|
permission.delete()
|
||||||
if approved:
|
if approved:
|
||||||
msg = _("You removed the permission.")
|
msg = _('You removed the permission.')
|
||||||
else:
|
else:
|
||||||
msg = _("You removed the permission request.")
|
msg = _('You removed the permission request.')
|
||||||
request.user.message_set.create(message=msg)
|
request.user.message_set.create(message=msg)
|
||||||
next = get_next(request)
|
next = get_next(request)
|
||||||
return HttpResponseRedirect(next)
|
return HttpResponseRedirect(next)
|
||||||
|
|
||||||
|
def permission_denied(request, template_name=None, extra_context={}):
|
||||||
def permission_denied(request, template_name=None, extra_context=None):
|
|
||||||
"""
|
"""
|
||||||
Default 403 handler.
|
Default 403 handler.
|
||||||
|
|
||||||
|
|
@ -120,14 +100,10 @@ def permission_denied(request, template_name=None, extra_context=None):
|
||||||
The path of the requested URL (e.g., '/app/pages/bad_page/')
|
The path of the requested URL (e.g., '/app/pages/bad_page/')
|
||||||
"""
|
"""
|
||||||
if template_name is None:
|
if template_name is None:
|
||||||
template_name = ("403.html", "authority/403.html")
|
template_name = ('403.html', 'authority/403.html')
|
||||||
context = {
|
context = {
|
||||||
"request_path": request.path,
|
'request_path': request.path,
|
||||||
}
|
}
|
||||||
if extra_context:
|
context.update(extra_context)
|
||||||
context.update(extra_context)
|
return HttpResponseForbidden(loader.render_to_string(template_name, context,
|
||||||
return HttpResponseForbidden(
|
context_instance=RequestContext(request)))
|
||||||
loader.render_to_string(
|
|
||||||
template_name=template_name, context=context, request=request,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
|
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
|
||||||
|
|
||||||
|
|
||||||
generic_script = """
|
generic_script = """
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function showGenericRelatedObjectLookupPopup(ct_select, triggering_link, url_base) {
|
function showGenericRelatedObjectLookupPopup(ct_select, triggering_link, url_base) {
|
||||||
|
|
@ -18,7 +17,6 @@ function showGenericRelatedObjectLookupPopup(ct_select, triggering_link, url_bas
|
||||||
</script>
|
</script>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class GenericForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
|
class GenericForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
|
||||||
def __init__(self, ct_field, cts=[], attrs=None):
|
def __init__(self, ct_field, cts=[], attrs=None):
|
||||||
self.ct_field = ct_field
|
self.ct_field = ct_field
|
||||||
|
|
@ -28,57 +26,28 @@ class GenericForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if attrs is None:
|
if attrs is None:
|
||||||
attrs = {}
|
attrs = {}
|
||||||
related_url = "../../../"
|
related_url = '../../../'
|
||||||
params = self.url_parameters()
|
params = self.url_parameters()
|
||||||
if params:
|
if params:
|
||||||
url = "?" + "&".join(["%s=%s" % (k, v) for k, v in params.iteritems()])
|
url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.iteritems()])
|
||||||
else:
|
else:
|
||||||
url = ""
|
url = ''
|
||||||
if "class" not in attrs:
|
if 'class' not in attrs:
|
||||||
attrs["class"] = "vForeignKeyRawIdAdminField"
|
attrs['class'] = 'vForeignKeyRawIdAdminField'
|
||||||
output = [forms.TextInput.render(self, name, value, attrs)]
|
output = [forms.TextInput.render(self, name, value, attrs)]
|
||||||
output.append(
|
output.append("""%(generic_script)s
|
||||||
"""%(generic_script)s
|
<a href="%(related)s%(url)s" class="related-lookup" id="lookup_id_%(name)s" onclick="return showGenericRelatedObjectLookupPopup(document.getElementById('id_%(ct_field)s'), this, '%(related)s%(url)s');"> """
|
||||||
<a href="%(related)s%(url)s"
|
% {'generic_script': generic_script, 'related': related_url, 'url': url, 'name': name, 'ct_field': self.ct_field})
|
||||||
class="related-lookup"
|
output.append('<img src="%s/admin/img/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.STATIC_URL, _('Lookup')))
|
||||||
id="lookup_id_%(name)s"
|
|
||||||
onclick="return showGenericRelatedObjectLookupPopup(
|
|
||||||
document.getElementById('id_%(ct_field)s'), this, '%(related)s%(url)s');">
|
|
||||||
"""
|
|
||||||
% {
|
|
||||||
"generic_script": generic_script,
|
|
||||||
"related": related_url,
|
|
||||||
"url": url,
|
|
||||||
"name": name,
|
|
||||||
"ct_field": self.ct_field,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
output.append(
|
|
||||||
'<img src="%s/admin/img/selector-search.gif" width="16" height="16" alt="%s" /></a>'
|
|
||||||
% (settings.STATIC_URL, _("Lookup"))
|
|
||||||
)
|
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
content_types = """
|
content_types = """
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var content_types = new Array();
|
var content_types = new Array();
|
||||||
%s
|
%s
|
||||||
</script>
|
</script>
|
||||||
""" % (
|
""" % ('\n'.join(["content_types[%s] = '%s/%s/';" % (ContentType.objects.get_for_model(ct).id, ct._meta.app_label, ct._meta.object_name.lower()) for ct in self.cts]))
|
||||||
"\n".join(
|
return mark_safe(u''.join(output) + content_types)
|
||||||
[
|
|
||||||
"content_types[%s] = '%s/%s/';"
|
|
||||||
% (
|
|
||||||
ContentType.objects.get_for_model(ct).id,
|
|
||||||
ct._meta.app_label,
|
|
||||||
ct._meta.object_name.lower(),
|
|
||||||
)
|
|
||||||
for ct in self.cts
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return mark_safe(u"".join(output) + content_types)
|
|
||||||
|
|
||||||
def url_parameters(self):
|
def url_parameters(self):
|
||||||
return {}
|
return {}
|
||||||
|
|
|
||||||
84
bootstrap.py
Normal file
84
bootstrap.py
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 Zope Corporation and Contributors.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# This software is subject to the provisions of the Zope Public License,
|
||||||
|
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||||
|
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||||
|
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
"""Bootstrap a buildout-based project
|
||||||
|
|
||||||
|
Simply run this script in a directory containing a buildout.cfg.
|
||||||
|
The script accepts buildout command-line options, so you can
|
||||||
|
use the -c option to specify an alternate configuration file.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, shutil, sys, tempfile, urllib2
|
||||||
|
|
||||||
|
tmpeggs = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
is_jython = sys.platform.startswith('java')
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
except ImportError:
|
||||||
|
ez = {}
|
||||||
|
exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
|
||||||
|
).read() in ez
|
||||||
|
ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
|
||||||
|
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
def quote(c):
|
||||||
|
if ' ' in c:
|
||||||
|
return '"%s"' % c # work around spawn lamosity on windows
|
||||||
|
else:
|
||||||
|
return c
|
||||||
|
else:
|
||||||
|
def quote (c):
|
||||||
|
return c
|
||||||
|
|
||||||
|
cmd = 'from setuptools.command.easy_install import main; main()'
|
||||||
|
ws = pkg_resources.working_set
|
||||||
|
|
||||||
|
if len(sys.argv) > 2 and sys.argv[1] == '--version':
|
||||||
|
VERSION = ' == %s' % sys.argv[2]
|
||||||
|
args = sys.argv[3:] + ['bootstrap']
|
||||||
|
else:
|
||||||
|
VERSION = ''
|
||||||
|
args = sys.argv[1:] + ['bootstrap']
|
||||||
|
|
||||||
|
if is_jython:
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
|
||||||
|
quote(tmpeggs), 'zc.buildout' + VERSION],
|
||||||
|
env=dict(os.environ,
|
||||||
|
PYTHONPATH=
|
||||||
|
ws.find(pkg_resources.Requirement.parse('setuptools')).location
|
||||||
|
),
|
||||||
|
).wait() == 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert os.spawnle(
|
||||||
|
os.P_WAIT, sys.executable, quote (sys.executable),
|
||||||
|
'-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
|
||||||
|
dict(os.environ,
|
||||||
|
PYTHONPATH=
|
||||||
|
ws.find(pkg_resources.Requirement.parse('setuptools')).location
|
||||||
|
),
|
||||||
|
) == 0
|
||||||
|
|
||||||
|
ws.add_entry(tmpeggs)
|
||||||
|
ws.require('zc.buildout' + VERSION)
|
||||||
|
import zc.buildout.buildout
|
||||||
|
zc.buildout.buildout.main(args)
|
||||||
|
shutil.rmtree(tmpeggs)
|
||||||
27
buildout.cfg
Normal file
27
buildout.cfg
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
[buildout]
|
||||||
|
parts =
|
||||||
|
python
|
||||||
|
django-1.0.X
|
||||||
|
django-1.1.X
|
||||||
|
develop = .
|
||||||
|
eggs =
|
||||||
|
django-authority
|
||||||
|
|
||||||
|
[python]
|
||||||
|
recipe = zc.recipe.egg
|
||||||
|
interpreter = python
|
||||||
|
eggs = ${buildout:eggs}
|
||||||
|
|
||||||
|
[django-1.0.X]
|
||||||
|
recipe = djangorecipe
|
||||||
|
version = 1.0.4
|
||||||
|
projectegg = example
|
||||||
|
settings = development
|
||||||
|
eggs = ${buildout:eggs}
|
||||||
|
|
||||||
|
[django-1.1.X]
|
||||||
|
recipe = djangorecipe
|
||||||
|
version = 1.1.1
|
||||||
|
projectegg = example
|
||||||
|
settings = development
|
||||||
|
eggs = ${buildout:eggs}
|
||||||
|
|
@ -28,7 +28,7 @@ Syntax::
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
{% ifhasperm "poll_permission.change_poll" request.user %}
|
{% if hasperm "poll_permission.change_poll" request.user %}
|
||||||
lalala
|
lalala
|
||||||
{% else %}
|
{% else %}
|
||||||
meh
|
meh
|
||||||
|
|
|
||||||
100
docs/conf.py
100
docs/conf.py
|
|
@ -11,13 +11,13 @@
|
||||||
# All configuration values have a default; values that are commented out
|
# All configuration values have a default; values that are commented out
|
||||||
# serve to show the default.
|
# serve to show the default.
|
||||||
|
|
||||||
from pkg_resources import get_distribution
|
import sys, os
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
# sys.path.append(os.path.abspath('.'))
|
#sys.path.append(os.path.abspath('.'))
|
||||||
# sys.path.append(os.path.join(os.path.dirname(__file__), '../src/'))
|
#sys.path.append(os.path.join(os.path.dirname(__file__), '../src/'))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -26,117 +26,118 @@ from pkg_resources import get_distribution
|
||||||
extensions = []
|
extensions = []
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
# templates_path = ['.templates']
|
#templates_path = ['.templates']
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = ".txt"
|
source_suffix = '.txt'
|
||||||
|
|
||||||
# The encoding of source files.
|
# The encoding of source files.
|
||||||
# source_encoding = 'utf-8'
|
#source_encoding = 'utf-8'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = "index"
|
master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u"django-authority"
|
project = u'django-authority'
|
||||||
copyright = u"2009-2020, Jannis Leidel"
|
copyright = u'2009, the django-authority team'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The full version, including alpha/beta/rc tags.
|
|
||||||
release = get_distribution("django-authority").version
|
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = ".".join(release.split(".")[:2])
|
version = '0.8'
|
||||||
|
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.8dev'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
# language = None
|
#language = None
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
# non-false value, then it is used:
|
# non-false value, then it is used:
|
||||||
# today = ''
|
#today = ''
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
# today_fmt = '%B %d, %Y'
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
# List of documents that shouldn't be included in the build.
|
# List of documents that shouldn't be included in the build.
|
||||||
# unused_docs = []
|
#unused_docs = []
|
||||||
|
|
||||||
# List of directories, relative to source directory, that shouldn't be searched
|
# List of directories, relative to source directory, that shouldn't be searched
|
||||||
# for source files.
|
# for source files.
|
||||||
exclude_trees = ["build"]
|
exclude_trees = ['build']
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
# default_role = None
|
#default_role = None
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
# add_function_parentheses = True
|
#add_function_parentheses = True
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
# If true, the current module name will be prepended to all description
|
||||||
# unit titles (such as .. function::).
|
# unit titles (such as .. function::).
|
||||||
# add_module_names = True
|
#add_module_names = True
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
# output. They are ignored by default.
|
# output. They are ignored by default.
|
||||||
# show_authors = False
|
#show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = "sphinx"
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
# modindex_common_prefix = []
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||||
html_theme = "nature"
|
html_theme = 'nature'
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
# documentation.
|
# documentation.
|
||||||
# html_theme_options = {}
|
#html_theme_options = {}
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
html_theme_path = [".theme"]
|
html_theme_path = ['.theme']
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
# "<project> v<release> documentation".
|
# "<project> v<release> documentation".
|
||||||
# html_title = None
|
#html_title = None
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
# html_short_title = None
|
#html_short_title = None
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
# of the sidebar.
|
# of the sidebar.
|
||||||
html_logo = ".static/logo.png"
|
html_logo = '.static/logo.png'
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
# pixels large.
|
# pixels large.
|
||||||
html_favicon = "favicon.png"
|
html_favicon = 'favicon.png'
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = [".static"]
|
html_static_path = ['.static']
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# using the given strftime format.
|
||||||
# html_last_updated_fmt = '%b %d, %Y'
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
html_use_smartypants = True
|
html_use_smartypants = True
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
# html_sidebars = {}
|
#html_sidebars = {}
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
# template names.
|
# template names.
|
||||||
# html_additional_pages = {}
|
#html_additional_pages = {}
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
html_use_modindex = True
|
html_use_modindex = True
|
||||||
|
|
@ -145,7 +146,7 @@ html_use_modindex = True
|
||||||
html_use_index = True
|
html_use_index = True
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
# If true, the index is split into individual pages for each letter.
|
||||||
# html_split_index = False
|
#html_split_index = False
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
# If true, links to the reST sources are added to the pages.
|
||||||
html_show_sourcelink = False
|
html_show_sourcelink = False
|
||||||
|
|
@ -153,48 +154,43 @@ html_show_sourcelink = False
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
# base URL from which the finished HTML is served.
|
# base URL from which the finished HTML is served.
|
||||||
# html_use_opensearch = ''
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
# html_file_suffix = ''
|
#html_file_suffix = ''
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = "django-authoritydoc"
|
htmlhelp_basename = 'django-authoritydoc'
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
# The paper size ('letter' or 'a4').
|
# The paper size ('letter' or 'a4').
|
||||||
# latex_paper_size = 'letter'
|
#latex_paper_size = 'letter'
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
# latex_font_size = '10pt'
|
#latex_font_size = '10pt'
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
(
|
('index', 'django-authority.tex', u'django-authority Documentation',
|
||||||
"index",
|
u'The django-authority team', 'manual'),
|
||||||
"django-authority.tex",
|
|
||||||
u"django-authority Documentation",
|
|
||||||
u"The django-authority team",
|
|
||||||
"manual",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
# the title page.
|
# the title page.
|
||||||
# latex_logo = None
|
#latex_logo = None
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
# not chapters.
|
# not chapters.
|
||||||
# latex_use_parts = False
|
#latex_use_parts = False
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
# Additional stuff for the LaTeX preamble.
|
||||||
# latex_preamble = ''
|
#latex_preamble = ''
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
# latex_appendices = []
|
#latex_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
# latex_use_modindex = True
|
#latex_use_modindex = True
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,12 @@ Let's start with an example::
|
||||||
|
|
||||||
import authority
|
import authority
|
||||||
from authority import permissions
|
from authority import permissions
|
||||||
from django.contrib.flatpages.models import FlatPage
|
from django.contrib.flatpages.models import Flatpage
|
||||||
|
|
||||||
class FlatpagePermission(permissions.BasePermission):
|
class FlatpagePermission(permissions.BasePermission):
|
||||||
label = 'flatpage_permission'
|
label = 'flatpage_permission'
|
||||||
|
|
||||||
authority.register(FlatPage, FlatpagePermission)
|
authority.register(Flatpage, FlatpagePermission)
|
||||||
|
|
||||||
Let's have a look at the code above. First of, if you want to create a new
|
Let's have a look at the code above. First of, if you want to create a new
|
||||||
permission you have to subclass it from the BasePermission class::
|
permission you have to subclass it from the BasePermission class::
|
||||||
|
|
@ -45,14 +45,14 @@ Next, you need to name this permission using the ``label`` attribute::
|
||||||
And finally you need to register the permission with the pool of all other
|
And finally you need to register the permission with the pool of all other
|
||||||
permissions::
|
permissions::
|
||||||
|
|
||||||
authority.register(FlatPage, FlatpagePermission)
|
authority.register(Flatpage, FlatpagePermission)
|
||||||
|
|
||||||
The syntax of this is simple::
|
The syntax of this is simple::
|
||||||
|
|
||||||
authority.register(<model>, <permission_class>)
|
authority.register(<model>, <permission_class>)
|
||||||
|
|
||||||
While this is not much code, you already wrapped Django's basic permissions
|
While this is not much code, you already wrapped Django's basic permissions
|
||||||
(add_flatpage, change_flatpage, delete_flatpage) for the model ``FlatPage``
|
(add_flatpage, change_flatpage, delete_flatpage) for the model ``Flatpage``
|
||||||
and you are ready to use it within your templates or code:
|
and you are ready to use it within your templates or code:
|
||||||
|
|
||||||
.. note:: See `Django's basic permissions`_ how Django creates this permissions for you.
|
.. note:: See `Django's basic permissions`_ how Django creates this permissions for you.
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ A custom permission is a simple method of the permission class::
|
||||||
Note that we first added the name of your custom permission to the ``checks``
|
Note that we first added the name of your custom permission to the ``checks``
|
||||||
attribute, like in :ref:`create-per-object-permission`::
|
attribute, like in :ref:`create-per-object-permission`::
|
||||||
|
|
||||||
checks = ('my_custom_check',)
|
checks = (my_custom_check',)
|
||||||
|
|
||||||
The permission itself is a simple function that accepts an arbitrary number of
|
The permission itself is a simple function that accepts an arbitrary number of
|
||||||
arguments. A permission class should always return a boolean whether the
|
arguments. A permission class should always return a boolean whether the
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ Development version
|
||||||
===================
|
===================
|
||||||
|
|
||||||
The latest development version is located on it's `Github account`_. You
|
The latest development version is located on it's `Github account`_. You
|
||||||
can checkout the package using the Git_ scm::
|
can checkout the package using the Mercurial_ scm::
|
||||||
|
|
||||||
git clone https://github.com/jazzband/django-authority
|
git clone https://github.com/jezdez/django-authority.git
|
||||||
|
|
||||||
Then install it manually::
|
Then install it manually::
|
||||||
|
|
||||||
|
|
@ -39,5 +39,5 @@ Then install it manually::
|
||||||
.. warning:: The development version is not fully tested and may contain
|
.. warning:: The development version is not fully tested and may contain
|
||||||
bugs, so we prefer to use the latest package from pypi.
|
bugs, so we prefer to use the latest package from pypi.
|
||||||
|
|
||||||
.. _Github account: https://github.com/jazzband/django-authority/
|
.. _Github account: https://github.com/jezdez/django-authority/
|
||||||
.. _Git: http://gitscm.org/
|
.. _Mercurial: http://www.selenic.com/mercurial/
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,4 @@ For more specific issues and bug reports please use the `issue tracker`_ on
|
||||||
django-authority's Github page.
|
django-authority's Github page.
|
||||||
|
|
||||||
.. _google group: http://groups.google.com/group/django-authority
|
.. _google group: http://groups.google.com/group/django-authority
|
||||||
.. _issue tracker: https://github.com/jazzband/django-authority/issues/
|
.. _issue tracker: https://github.com/jezdez/django-authority/issues/
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from example.settings import *
|
|
||||||
|
|
||||||
DEBUG = True
|
from example.settings import *
|
||||||
TEMPLATE_DEBUG = DEBUG
|
DEBUG=True
|
||||||
|
TEMPLATE_DEBUG=DEBUG
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,5 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from authority.forms import UserPermissionForm
|
from authority.forms import UserPermissionForm
|
||||||
|
|
||||||
|
|
||||||
class SpecialUserPermissionForm(UserPermissionForm):
|
class SpecialUserPermissionForm(UserPermissionForm):
|
||||||
user = forms.CharField(label=_("Special user"), widget=forms.Textarea())
|
user = forms.CharField(label=_('Special user'), widget=forms.Textarea())
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
import authority
|
import authority
|
||||||
from authority.permissions import BasePermission
|
from authority.permissions import BasePermission
|
||||||
|
|
||||||
|
|
||||||
class FlatPagePermission(BasePermission):
|
class FlatPagePermission(BasePermission):
|
||||||
"""
|
"""
|
||||||
This class contains a bunch of checks:
|
This class contains a bunch of checks:
|
||||||
|
|
||||||
1. the default checks 'add_flatpage', 'browse_flatpage',
|
1. the default checks 'add_flatpage', 'browse_flatpage',
|
||||||
'change_flatpage' and 'delete_flatpage'
|
'change_flatpage' and 'delete_flatpage'
|
||||||
2. the custom checks:
|
2. the custom checks:
|
||||||
|
|
@ -41,16 +40,13 @@ class FlatPagePermission(BasePermission):
|
||||||
{% endifhasperm %}
|
{% endifhasperm %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
label = 'flatpage_permission'
|
||||||
label = "flatpage_permission"
|
checks = ('review', 'top_secret')
|
||||||
checks = ("review", "top_secret")
|
|
||||||
|
|
||||||
def top_secret(self, flatpage=None, lala=None):
|
def top_secret(self, flatpage=None, lala=None):
|
||||||
if flatpage and flatpage.registration_required:
|
if flatpage and flatpage.registration_required:
|
||||||
return self.browse_flatpage(obj=flatpage)
|
return self.browse_flatpage(obj=flatpage)
|
||||||
return False
|
return False
|
||||||
|
top_secret.short_description=_('Is allowed to see top secret flatpages')
|
||||||
|
|
||||||
top_secret.short_description = _("Is allowed to see top secret flatpages")
|
authority.register(FlatPage, FlatPagePermission)
|
||||||
|
|
||||||
|
|
||||||
authority.sites.register(FlatPage, FlatPagePermission)
|
|
||||||
|
|
|
||||||
23
example/exampleapp/tests.py
Normal file
23
example/exampleapp/tests.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
"""
|
||||||
|
This file demonstrates two different styles of tests (one doctest and one
|
||||||
|
unittest). These will both pass when you run "manage.py test".
|
||||||
|
|
||||||
|
Replace these with more appropriate tests for your application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
class SimpleTest(TestCase):
|
||||||
|
def test_basic_addition(self):
|
||||||
|
"""
|
||||||
|
Tests that 1 + 1 always equals 2.
|
||||||
|
"""
|
||||||
|
self.failUnlessEqual(1 + 1, 2)
|
||||||
|
|
||||||
|
__test__ = {"doctest": """
|
||||||
|
Another way to test that 1 + 1 is equal to 2.
|
||||||
|
|
||||||
|
>>> 1 + 1 == 2
|
||||||
|
True
|
||||||
|
"""}
|
||||||
|
|
||||||
|
|
@ -8,9 +8,8 @@ from authority.decorators import permission_required, permission_required_or_403
|
||||||
# @permission_required('flatpage_permission.top_secret',
|
# @permission_required('flatpage_permission.top_secret',
|
||||||
# (FlatPage, 'url__contains', 'url'), (FlatPage, 'url__contains', 'lala'))
|
# (FlatPage, 'url__contains', 'url'), (FlatPage, 'url__contains', 'lala'))
|
||||||
# use this to return a 403 page:
|
# use this to return a 403 page:
|
||||||
@permission_required_or_403(
|
@permission_required_or_403('flatpage_permission.top_secret',
|
||||||
"flatpage_permission.top_secret", (FlatPage, "url__contains", "url"), "lala"
|
(FlatPage, 'url__contains', 'url'), 'lala')
|
||||||
)
|
|
||||||
def top_secret(request, url, lala=None):
|
def top_secret(request, url, lala=None):
|
||||||
"""
|
"""
|
||||||
A wrapping view that performs the permission check given in the decorator
|
A wrapping view that performs the permission check given in the decorator
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,13 @@ from django.core.management import execute_from_command_line
|
||||||
try:
|
try:
|
||||||
import settings as settings_mod # Assumed to be in the same directory.
|
import settings as settings_mod # Assumed to be in the same directory.
|
||||||
except ImportError:
|
except ImportError:
|
||||||
sys.stderr.write(
|
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
||||||
"Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n"
|
|
||||||
% __file__
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
sys.path.insert(0, settings_mod.PROJECT_ROOT)
|
sys.path.insert(0, settings_mod.PROJECT_ROOT)
|
||||||
sys.path.insert(0, settings_mod.PROJECT_ROOT + "/../")
|
sys.path.insert(0, settings_mod.PROJECT_ROOT + '/../')
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", 'example.settings')
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
execute_from_command_line(sys.argv)
|
execute_from_command_line(sys.argv)
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
|
||||||
from example.settings import *
|
from example.settings import *
|
||||||
|
|
|
||||||
|
|
@ -11,87 +11,86 @@ ADMINS = (
|
||||||
MANAGERS = ADMINS
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
'default': {
|
||||||
"ENGINE": "django.db.backends.sqlite3",
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
"NAME": os.path.join(PROJECT_ROOT, "example.db"),
|
'NAME': os.path.join(PROJECT_ROOT, 'example.db'),
|
||||||
"USER": "",
|
'USER': '',
|
||||||
"PASSWORD": "",
|
'PASSWORD': '',
|
||||||
"HOST": "",
|
'HOST': '',
|
||||||
"PORT": "",
|
'PORT': '',
|
||||||
"TEST": {"NAME": ":memory:", "ENGINE": "django.db.backends.sqlite3",},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CACHES = {"default": {"BACKEND": "django.core.cache.backends.dummy.DummyCache",}}
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TIME_ZONE = "America/Chicago"
|
TIME_ZONE = 'America/Chicago'
|
||||||
|
|
||||||
LANGUAGE_CODE = "en-us"
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
# Absolute path to the directory that holds media.
|
# Absolute path to the directory that holds media.
|
||||||
# Example: "/home/media/media.lawrence.com/"
|
# Example: "/home/media/media.lawrence.com/"
|
||||||
MEDIA_ROOT = os.path.join(PROJECT_ROOT, "media")
|
MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media')
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||||
# trailing slash if there is a path component (optional in other cases).
|
# trailing slash if there is a path component (optional in other cases).
|
||||||
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
||||||
MEDIA_URL = "/media/"
|
MEDIA_URL = '/media/'
|
||||||
|
|
||||||
# Don't share this with anybody.
|
# Don't share this with anybody.
|
||||||
SECRET_KEY = "ljlv2lb2d&)#by6th=!v=03-c^(o4lop92i@z4b3f1&ve0yx6d"
|
SECRET_KEY = 'ljlv2lb2d&)#by6th=!v=03-c^(o4lop92i@z4b3f1&ve0yx6d'
|
||||||
|
|
||||||
MIDDLEWARE = (
|
MIDDLEWARE_CLASSES = (
|
||||||
"django.middleware.common.CommonMiddleware",
|
'django.middleware.common.CommonMiddleware',
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
#'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
|
||||||
# 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
INTERNAL_IPS = ("127.0.0.1",)
|
INTERNAL_IPS = ('127.0.0.1',)
|
||||||
|
|
||||||
|
|
||||||
TEMPLATE_CONTEXT_PROCESSORS = ()
|
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
|
'django.core.context_processors.auth',
|
||||||
|
'django.core.context_processors.debug',
|
||||||
|
'django.core.context_processors.i18n',
|
||||||
|
'django.core.context_processors.media',
|
||||||
|
'django.core.context_processors.request',
|
||||||
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = "example.urls"
|
ROOT_URLCONF = 'example.urls'
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
"django.contrib.auth",
|
'django.contrib.auth',
|
||||||
"django.contrib.contenttypes",
|
'django.contrib.contenttypes',
|
||||||
"django.contrib.sessions",
|
'django.contrib.sessions',
|
||||||
"django.contrib.sites",
|
'django.contrib.sites',
|
||||||
"django.contrib.flatpages",
|
'django.contrib.flatpages',
|
||||||
"django.contrib.messages",
|
'django.contrib.admin',
|
||||||
"django.contrib.admin",
|
'authority',
|
||||||
"authority",
|
'example.exampleapp',
|
||||||
"example.exampleapp",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if VERSION >= (1, 5):
|
if VERSION >= (1, 5):
|
||||||
INSTALLED_APPS = INSTALLED_APPS + ("example.users",)
|
INSTALLED_APPS = INSTALLED_APPS + ('example.users',)
|
||||||
AUTH_USER_MODEL = "users.User"
|
AUTH_USER_MODEL = 'users.User'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATE_LOADERS = (
|
||||||
{
|
'django.template.loaders.filesystem.load_template_source',
|
||||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
'django.template.loaders.app_directories.load_template_source',
|
||||||
"DIRS": [
|
)
|
||||||
# insert your TEMPLATE_DIRS here
|
|
||||||
],
|
TEMPLATE_DIRS = (
|
||||||
"APP_DIRS": True,
|
os.path.join(PROJECT_ROOT, "templates"),
|
||||||
"OPTIONS": {
|
)
|
||||||
"context_processors": [
|
|
||||||
# Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this
|
# Use local_settings.py for things to override privately
|
||||||
# list if you haven't customized them:
|
try:
|
||||||
"django.contrib.auth.context_processors.auth",
|
from local_settings import * # noqa
|
||||||
"django.template.context_processors.debug",
|
except ImportError:
|
||||||
"django.template.context_processors.i18n",
|
pass
|
||||||
"django.template.context_processors.media",
|
|
||||||
"django.template.context_processors.static",
|
|
||||||
"django.template.context_processors.tz",
|
|
||||||
"django.contrib.messages.context_processors.messages",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,39 @@
|
||||||
import django.contrib.auth.views
|
try:
|
||||||
from django.conf.urls import include, handler500, url
|
from django.conf.urls import patterns, include, handler500, url
|
||||||
|
except ImportError: # django < 1.4
|
||||||
|
from django.conf.urls.defaults import patterns, include, handler500, url
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib import admin
|
||||||
|
import authority
|
||||||
|
|
||||||
import authority.views
|
admin.autodiscover()
|
||||||
import authority.urls
|
authority.autodiscover()
|
||||||
import example.exampleapp.views
|
|
||||||
|
handler500 # Pyflakes
|
||||||
|
|
||||||
from exampleapp.forms import SpecialUserPermissionForm
|
from exampleapp.forms import SpecialUserPermissionForm
|
||||||
|
|
||||||
authority.autodiscover()
|
urlpatterns = patterns('',
|
||||||
|
(r'^admin/(.*)', admin.site.root),
|
||||||
handler500 # flake8
|
#('^admin/', include(admin.site.urls)),
|
||||||
|
url(r'^authority/permission/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$',
|
||||||
urlpatterns = (
|
view='authority.views.add_permission',
|
||||||
url(
|
|
||||||
r"^authority/permission/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$", # noqa
|
|
||||||
view=authority.views.add_permission,
|
|
||||||
name="authority-add-permission",
|
name="authority-add-permission",
|
||||||
kwargs={"approved": True, "form_class": SpecialUserPermissionForm},
|
kwargs={'approved': True, 'form_class': SpecialUserPermissionForm}
|
||||||
),
|
),
|
||||||
url(
|
url(r'^request/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$',
|
||||||
r"^request/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$", # noqa
|
view='authority.views.add_permission',
|
||||||
view=authority.views.add_permission,
|
|
||||||
name="authority-add-permission-request",
|
name="authority-add-permission-request",
|
||||||
kwargs={"approved": False, "form_class": SpecialUserPermissionForm},
|
kwargs={'approved': False, 'form_class': SpecialUserPermissionForm}
|
||||||
),
|
|
||||||
url(r"^authority/", include(authority.urls)),
|
|
||||||
url(r"^accounts/login/$", django.contrib.auth.views.LoginView.as_view()),
|
|
||||||
url(
|
|
||||||
r"^(?P<url>[\/0-9A-Za-z]+)$",
|
|
||||||
example.exampleapp.views.top_secret,
|
|
||||||
{"lala": "oh yeah!"},
|
|
||||||
),
|
),
|
||||||
|
(r'^authority/', include('authority.urls')),
|
||||||
|
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
|
||||||
|
url(r'^(?P<url>[\/0-9A-Za-z]+)$', 'example.exampleapp.views.top_secret', {'lala': 'oh yeah!'}),
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += (
|
urlpatterns += patterns('',
|
||||||
url(
|
(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
|
||||||
r"^media/(?P<path>.*)$",
|
'document_root': settings.MEDIA_ROOT,
|
||||||
django.views.static.serve,
|
}),
|
||||||
{"document_root": settings.MEDIA_ROOT,},
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from django.contrib import admin
|
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
from example.users.models import User
|
from example.users.User
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
|
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.22 on 2019-07-12 16:01
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import django.contrib.auth.models
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.utils.timezone
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("auth", "0008_alter_user_username_max_length"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="User",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.AutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("password", models.CharField(max_length=128, verbose_name="password")),
|
|
||||||
(
|
|
||||||
"last_login",
|
|
||||||
models.DateTimeField(
|
|
||||||
blank=True, null=True, verbose_name="last login"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"is_superuser",
|
|
||||||
models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="Designates that this user has all permissions without explicitly assigning them.",
|
|
||||||
verbose_name="superuser status",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("username", models.CharField(max_length=100)),
|
|
||||||
("first_name", models.CharField(max_length=50)),
|
|
||||||
("last_name", models.CharField(max_length=50)),
|
|
||||||
("email", models.EmailField(max_length=254, unique=True)),
|
|
||||||
("greeting_message", models.TextField()),
|
|
||||||
("is_staff", models.BooleanField(default=False)),
|
|
||||||
("is_active", models.BooleanField(default=True)),
|
|
||||||
(
|
|
||||||
"date_joined",
|
|
||||||
models.DateTimeField(default=django.utils.timezone.now),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"groups",
|
|
||||||
models.ManyToManyField(
|
|
||||||
blank=True,
|
|
||||||
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
|
|
||||||
related_name="user_set",
|
|
||||||
related_query_name="user",
|
|
||||||
to="auth.Group",
|
|
||||||
verbose_name="groups",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"user_permissions",
|
|
||||||
models.ManyToManyField(
|
|
||||||
blank=True,
|
|
||||||
help_text="Specific permissions for this user.",
|
|
||||||
related_name="user_set",
|
|
||||||
related_query_name="user",
|
|
||||||
to="auth.Permission",
|
|
||||||
verbose_name="user permissions",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={"abstract": False,},
|
|
||||||
managers=[("objects", django.contrib.auth.models.UserManager()),],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
|
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
|
||||||
from django.contrib.auth.models import UserManager
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractBaseUser, PermissionsMixin):
|
class User(AbstractBaseUser, PermissionsMixin):
|
||||||
USERNAME_FIELD = "email"
|
USERNAME_FIELD = 'email'
|
||||||
REQUIRED_FIELDS = ["first_name", "last_name"]
|
REQUIRED_FIELDS = ['first_name', 'last_name']
|
||||||
|
|
||||||
username = models.CharField(max_length=100)
|
|
||||||
first_name = models.CharField(max_length=50)
|
first_name = models.CharField(max_length=50)
|
||||||
last_name = models.CharField(max_length=50)
|
last_name = models.CharField(max_length=50)
|
||||||
email = models.EmailField(unique=True)
|
email = models.EmailField(unique=True)
|
||||||
|
|
@ -16,5 +14,3 @@ class User(AbstractBaseUser, PermissionsMixin):
|
||||||
is_staff = models.BooleanField(default=False)
|
is_staff = models.BooleanField(default=False)
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
date_joined = models.DateTimeField(default=timezone.now)
|
date_joined = models.DateTimeField(default=timezone.now)
|
||||||
|
|
||||||
objects = UserManager()
|
|
||||||
|
|
|
||||||
16
migrations/0.2-0.3.mysql.sql
Normal file
16
migrations/0.2-0.3.mysql.sql
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
BEGIN;
|
||||||
|
-- Application: authority
|
||||||
|
-- Model: Permission
|
||||||
|
ALTER TABLE `authority_permission`
|
||||||
|
ADD `date_requested` DATETIME;
|
||||||
|
ALTER TABLE `authority_permission`
|
||||||
|
ADD `approved` BOOL;
|
||||||
|
ALTER TABLE `authority_permission`
|
||||||
|
ADD `date_approved` DATETIME;
|
||||||
|
ALTER TABLE `authority_permission`
|
||||||
|
MODIFY `object_id` INTEGER UNSIGNED;
|
||||||
|
UPDATE `authority_permission`
|
||||||
|
SET `approved` = TRUE;
|
||||||
|
UPDATE `authority_permission`
|
||||||
|
SET `date_approved` = NOW();
|
||||||
|
COMMIT;
|
||||||
14
migrations/0.2-0.3.postgres.sql
Normal file
14
migrations/0.2-0.3.postgres.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
BEGIN;
|
||||||
|
-- Application: authority
|
||||||
|
-- Model: Permission
|
||||||
|
ALTER TABLE "authority_permission"
|
||||||
|
ADD "date_requested" timestamp with time zone;
|
||||||
|
ALTER TABLE "authority_permission"
|
||||||
|
ADD "approved" boolean;
|
||||||
|
ALTER TABLE "authority_permission"
|
||||||
|
ADD "date_approved" timestamp with time zone;
|
||||||
|
UPDATE "authority_permission"
|
||||||
|
SET "approved" = True;
|
||||||
|
UPDATE "authority_permission"
|
||||||
|
SET "date_approved" = NOW();
|
||||||
|
COMMIT;
|
||||||
14
migrations/0.2-0.3.sqlite.sql
Normal file
14
migrations/0.2-0.3.sqlite.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
BEGIN;
|
||||||
|
-- Application: authority
|
||||||
|
-- Model: Permission
|
||||||
|
ALTER TABLE "authority_permission"
|
||||||
|
ADD "date_requested" datetime;
|
||||||
|
ALTER TABLE "authority_permission"
|
||||||
|
ADD "approved" bool;
|
||||||
|
ALTER TABLE "authority_permission"
|
||||||
|
ADD "date_approved" datetime;
|
||||||
|
UPDATE "authority_permission"
|
||||||
|
SET "approved" = 1;
|
||||||
|
UPDATE "authority_permission"
|
||||||
|
SET "date_approved" = DATETIME("NOW");
|
||||||
|
COMMIT;
|
||||||
|
|
@ -8,6 +8,3 @@ all_files = 1
|
||||||
|
|
||||||
[upload_docs]
|
[upload_docs]
|
||||||
upload-dir = docs/build/html
|
upload-dir = docs/build/html
|
||||||
|
|
||||||
[wheel]
|
|
||||||
universal = 1
|
|
||||||
|
|
|
||||||
53
setup.py
53
setup.py
|
|
@ -5,38 +5,39 @@ from setuptools import setup, find_packages
|
||||||
def read(fname):
|
def read(fname):
|
||||||
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="django-authority",
|
name='django-authority',
|
||||||
use_scm_version=True,
|
version='0.8',
|
||||||
description=(
|
description=(
|
||||||
"A Django app that provides generic per-object-permissions "
|
"A Django app that provides generic per-object-permissions "
|
||||||
"for Django's auth app."
|
"for Django's auth app."
|
||||||
),
|
),
|
||||||
long_description=read("README.rst"),
|
long_description=read('README.rst'),
|
||||||
long_description_content_type="text/x-rst",
|
author='Jannis Leidel',
|
||||||
author="Jannis Leidel",
|
author_email='jannis@leidel.info',
|
||||||
author_email="jannis@leidel.info",
|
license='BSD',
|
||||||
license="BSD",
|
url='https://github.com/jezdez/django-authority/',
|
||||||
url="https://github.com/jazzband/django-authority/",
|
packages=find_packages(),
|
||||||
packages=find_packages(exclude=("example", "example.*")),
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 5 - Production/Stable",
|
'Development Status :: 3 - Alpha',
|
||||||
"Environment :: Web Environment",
|
'Environment :: Web Environment',
|
||||||
"Intended Audience :: Developers",
|
'Intended Audience :: Developers',
|
||||||
"License :: OSI Approved :: BSD License",
|
'License :: OSI Approved :: BSD License',
|
||||||
"Operating System :: OS Independent",
|
'Operating System :: OS Independent',
|
||||||
"Programming Language :: Python",
|
'Programming Language :: Python',
|
||||||
"Programming Language :: Python :: 2",
|
'Programming Language :: Python :: 2.6',
|
||||||
"Programming Language :: Python :: 2.7",
|
'Programming Language :: Python :: 2.7',
|
||||||
"Programming Language :: Python :: 3",
|
'Programming Language :: Python :: 3.3',
|
||||||
"Programming Language :: Python :: 3.6",
|
'Framework :: Django',
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Framework :: Django",
|
|
||||||
],
|
],
|
||||||
install_requires=["django"],
|
install_requires=['django'],
|
||||||
setup_requires=["setuptools_scm"],
|
package_data = {
|
||||||
include_package_data=True,
|
'authority': [
|
||||||
|
'fixtures/test.json',
|
||||||
|
'templates/authority/*.html',
|
||||||
|
'templates/admin/edit_inline/action_tabular.html',
|
||||||
|
'templates/admin/permission_change_form.html',
|
||||||
|
]
|
||||||
|
},
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
39
tox.ini
39
tox.ini
|
|
@ -1,39 +0,0 @@
|
||||||
[tox]
|
|
||||||
skipsdist = True
|
|
||||||
usedevelop = True
|
|
||||||
minversion = 1.8
|
|
||||||
envlist =
|
|
||||||
py27-dj111
|
|
||||||
py37-dj{111,22}
|
|
||||||
{py36,py37,py38}-dj{30,31}
|
|
||||||
py37-check
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
usedevelop = true
|
|
||||||
commands =
|
|
||||||
coverage run -a example/manage.py test authority exampleapp
|
|
||||||
coverage report
|
|
||||||
coverage xml
|
|
||||||
deps =
|
|
||||||
coverage
|
|
||||||
dj111: Django>=1.11,<2.0
|
|
||||||
dj22: Django>=2.2,<2.3
|
|
||||||
dj30: Django>=3.0,<3.1
|
|
||||||
dj31: Django>=3.1,<3.2
|
|
||||||
|
|
||||||
|
|
||||||
[testenv:py37-check]
|
|
||||||
deps =
|
|
||||||
twine
|
|
||||||
wheel
|
|
||||||
commands =
|
|
||||||
python setup.py sdist bdist_wheel
|
|
||||||
twine check dist/*
|
|
||||||
|
|
||||||
|
|
||||||
[gh-actions]
|
|
||||||
python =
|
|
||||||
2.7: py27
|
|
||||||
3.6: py36
|
|
||||||
3.7: py37
|
|
||||||
3.8: py38
|
|
||||||
Loading…
Reference in a new issue