exceptional-auth 0.3.1__py2-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exceptional_auth/README.md +7 -0
- exceptional_auth/RELEASE_NOTES.md +12 -0
- exceptional_auth/VERSION +1 -0
- exceptional_auth/__init__.py +125 -0
- exceptional_auth/__pycache__/__init__.cpython-36.pyc +0 -0
- exceptional_auth-0.3.1.dist-info/LICENSE +19 -0
- exceptional_auth-0.3.1.dist-info/METADATA +24 -0
- exceptional_auth-0.3.1.dist-info/RECORD +10 -0
- exceptional_auth-0.3.1.dist-info/WHEEL +5 -0
- exceptional_auth-0.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# exceptional_auth
|
|
2
|
+
|
|
3
|
+
The purpose of this package is to provide a more flexible/powerful replacement for Django's `login_required` and `permission_required` decorators (and provide some entirely new related functionality).
|
|
4
|
+
|
|
5
|
+
The idea is to create a standard set of exceptions which any code can raise (even reusable apps distributed on pypi), and leave the handling of these exceptions up to the site developer (via custom middleware). This makes the many per-view decisions easy (ie. just raise PermissionDenied), while letting you centralize/delay the decision of what to do in those situations. Since you have access to the request object in the middleware methods, you can easily tailor the handling of these exceptions base on section of site, request type, etc.
|
|
6
|
+
|
|
7
|
+
We provide `exceptional_auth.BaseMiddleware`, a Middleware base class which makes it easier to handle our exceptions. Site developers should write a custom middleware extending this class, and add to `MIDDLEWARE` setting. You can also use `exceptional_auth.BaseMiddleware` to start with, and then extend it later.
|
exceptional_auth/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.3.1
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from django import http
|
|
2
|
+
from django.utils.translation import gettext as _
|
|
3
|
+
|
|
4
|
+
class AuthException(Exception):
|
|
5
|
+
'''
|
|
6
|
+
Base class for all of our exceptions.
|
|
7
|
+
|
|
8
|
+
Has no particular meaning, other than "the current user cannot currently access this resource".
|
|
9
|
+
'''
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
class LoginRequired(AuthException):
|
|
13
|
+
'''
|
|
14
|
+
The user is not logged in, and must log in to access this resource.
|
|
15
|
+
|
|
16
|
+
App developers are not expected to instantiate this directly. Call require_login() instead.
|
|
17
|
+
|
|
18
|
+
Site developers should catch this and provide the user with the opportunity to log in.
|
|
19
|
+
'''
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
class PermissionDenied(AuthException):
|
|
23
|
+
'''
|
|
24
|
+
Indicates that the user is logged in, but lacks some necessary permission.
|
|
25
|
+
|
|
26
|
+
App developers should call require_permission() when checking for standard django permissions, but they may also raise this directly when performing other custom permission checks.
|
|
27
|
+
'''
|
|
28
|
+
def __init__(self, message=None):
|
|
29
|
+
self.message = message
|
|
30
|
+
|
|
31
|
+
class NotCurrentlyAllowed(AuthException):
|
|
32
|
+
'''
|
|
33
|
+
DEPRECATED. We recommend using Conflict, instead.
|
|
34
|
+
|
|
35
|
+
Indicates that the user has all necessary permissions, but that the request cannot be handled because of some other time-based/logic constraints.
|
|
36
|
+
|
|
37
|
+
Site developers should catch this and display a generic error message to the user.
|
|
38
|
+
'''
|
|
39
|
+
def __init__(self, reason):
|
|
40
|
+
self.reason = reason
|
|
41
|
+
|
|
42
|
+
class Conflict(AuthException):
|
|
43
|
+
'''
|
|
44
|
+
The operation couldn't proceed because of some type of conflicting state
|
|
45
|
+
(registration window hasn't opened yet, you must delete related objects first, etc.).
|
|
46
|
+
|
|
47
|
+
Note that this could be thrown from anywhere (even model code).
|
|
48
|
+
Expect the message to be presented directly to the end user.
|
|
49
|
+
'''
|
|
50
|
+
def __init__(self, message, code=None):
|
|
51
|
+
'''
|
|
52
|
+
message should fully decribe why action can't proceed,
|
|
53
|
+
and, ideally, what user can do to fix it.
|
|
54
|
+
Expect message to be shown directly to the end user.
|
|
55
|
+
It may be a translatable string.
|
|
56
|
+
|
|
57
|
+
code may be set, and can be used by higher levels of code
|
|
58
|
+
(ie. view code) to differentiate between types of Conflicts and
|
|
59
|
+
render more specific error message, if desired.
|
|
60
|
+
'''
|
|
61
|
+
self.message = message
|
|
62
|
+
self.code = code
|
|
63
|
+
def __str__(self):
|
|
64
|
+
return str(self.message)
|
|
65
|
+
|
|
66
|
+
def require_login(request):
|
|
67
|
+
if request.user.is_anonymous :
|
|
68
|
+
raise LoginRequired()
|
|
69
|
+
|
|
70
|
+
def require_permissions(request, *permission_names):
|
|
71
|
+
require_login(request)
|
|
72
|
+
user = request.user
|
|
73
|
+
for permission_name in permission_names :
|
|
74
|
+
# TODO - if DEBUG, should we validate that permission_name is actually a valid permission name?
|
|
75
|
+
if not user.has_perm(permission_name) :
|
|
76
|
+
raise PermissionDenied()
|
|
77
|
+
|
|
78
|
+
class BaseMiddleware:
|
|
79
|
+
'''
|
|
80
|
+
A Middleware base class which site developers can extend, to make handling our exceptions easier.
|
|
81
|
+
'''
|
|
82
|
+
|
|
83
|
+
# Boilerplate, required by all middleware
|
|
84
|
+
def __init__(self, get_response):
|
|
85
|
+
self.get_response = get_response
|
|
86
|
+
def __call__(self, request):
|
|
87
|
+
return self.get_response(request)
|
|
88
|
+
|
|
89
|
+
def process_exception(self, request, exception):
|
|
90
|
+
if isinstance(exception, LoginRequired) :
|
|
91
|
+
return self.login_required(request, exception)
|
|
92
|
+
if isinstance(exception, PermissionDenied) :
|
|
93
|
+
return self.permission_denied(request, exception)
|
|
94
|
+
if isinstance(exception, NotCurrentlyAllowed) :
|
|
95
|
+
return self.not_currently_allowed(request, exception)
|
|
96
|
+
if isinstance(exception, Conflict) :
|
|
97
|
+
return self.conflict(request, exception)
|
|
98
|
+
|
|
99
|
+
# Site developers should override these methods
|
|
100
|
+
def login_required(self, request, exception):
|
|
101
|
+
return http.HttpResponse(_('Login required.'), content_type='text/plain')
|
|
102
|
+
def permission_denied(self, request, exception):
|
|
103
|
+
return http.HttpResponse(_('Permission denied.'), content_type='text/plain', status=403)
|
|
104
|
+
def not_currently_allowed(self, request, exception):
|
|
105
|
+
return http.HttpResponse(f'{_("Not currently allowed")}: {exception.reason}', content_type='text/plain', status=403)
|
|
106
|
+
|
|
107
|
+
def conflict(self, request, exception):
|
|
108
|
+
'''
|
|
109
|
+
This implementation likely doesn't need to be overridden.
|
|
110
|
+
Conflicts may be caught by view code to provide specific error messaging.
|
|
111
|
+
This implementation is probably good enough as a general fallback.
|
|
112
|
+
|
|
113
|
+
Note - if using jsform, this response works well if you add the
|
|
114
|
+
following to your base javascript:
|
|
115
|
+
|
|
116
|
+
addEventListener('jsformerror', function(e) {
|
|
117
|
+
var r = e.detail;
|
|
118
|
+
if (r.status == 409 && r.responseText) {
|
|
119
|
+
alert(r.responseText);
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
e.target.removeAttribute('block-submissions');
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
'''
|
|
125
|
+
return http.HttpResponse(exception.message, content_type='text/plain', status=409)
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2020 Quadrant Newmedia Corporation
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: exceptional-auth
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Exception-based authentication helpers for django
|
|
5
|
+
Home-page: https://github.com/quadrant-newmedia/exceptional_auth
|
|
6
|
+
Author: Alex Fischer
|
|
7
|
+
Author-email: alex@quadrant.net
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.6
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: Django (<5,>=2.2)
|
|
16
|
+
|
|
17
|
+
# exceptional_auth
|
|
18
|
+
|
|
19
|
+
The purpose of this package is to provide a more flexible/powerful replacement for Django's `login_required` and `permission_required` decorators (and provide some entirely new related functionality).
|
|
20
|
+
|
|
21
|
+
The idea is to create a standard set of exceptions which any code can raise (even reusable apps distributed on pypi), and leave the handling of these exceptions up to the site developer (via custom middleware). This makes the many per-view decisions easy (ie. just raise PermissionDenied), while letting you centralize/delay the decision of what to do in those situations. Since you have access to the request object in the middleware methods, you can easily tailor the handling of these exceptions base on section of site, request type, etc.
|
|
22
|
+
|
|
23
|
+
We provide `exceptional_auth.BaseMiddleware`, a Middleware base class which makes it easier to handle our exceptions. Site developers should write a custom middleware extending this class, and add to `MIDDLEWARE` setting. You can also use `exceptional_auth.BaseMiddleware` to start with, and then extend it later.
|
|
24
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
exceptional_auth/README.md,sha256=d0y73M6HEIgzo-5LPXW2iJI66yl2wTIcrMZVyRZF3AU,1079
|
|
2
|
+
exceptional_auth/RELEASE_NOTES.md,sha256=cF68viNJ-U0RwnW2TC2iKvBUEltAb51pVnrQHn_uPSg,225
|
|
3
|
+
exceptional_auth/VERSION,sha256=9PyVbsYbGHzRh5DAsKb6XR06MN5KVJPjJ3RgSF7RiPI,5
|
|
4
|
+
exceptional_auth/__init__.py,sha256=bhieHDlK8uzmP35v9qFglnlegmmSU6yEBBeX3mmk5Is,4981
|
|
5
|
+
exceptional_auth/__pycache__/__init__.cpython-36.pyc,sha256=hehRHpJSEuP2wa4_qDSllXccNhQPE5Tiu6rkXQelWqA,3927
|
|
6
|
+
exceptional_auth-0.3.1.dist-info/LICENSE,sha256=3zadg6RilfqcoXgQ6j6BXFbJBGZbjlpiAxtVboyrBQk,1072
|
|
7
|
+
exceptional_auth-0.3.1.dist-info/METADATA,sha256=E-0-DJt2cfebY0hzZER-hwpWmrj2b2FGevcZpKCLVrs,1596
|
|
8
|
+
exceptional_auth-0.3.1.dist-info/WHEEL,sha256=1VPi6hfNQaRRNuEdK_3dv9o8COtLGnHWJghhj4CQ28k,92
|
|
9
|
+
exceptional_auth-0.3.1.dist-info/top_level.txt,sha256=7X4XuoTCS3lzq_v2FzKA0Prp3PdwRiRFEVkaiNmJnZ8,17
|
|
10
|
+
exceptional_auth-0.3.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exceptional_auth
|