all: implement black as code formatter
This commit is contained in:
parent
8eb3f0f708
commit
3bd1eadd51
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
|||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
- name: Lint with pylint
|
||||
run: pipenv run pylint passbook
|
||||
isort:
|
||||
black:
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
@ -41,8 +41,8 @@ jobs:
|
|||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
- name: Lint with isort
|
||||
run: pipenv run isort -c
|
||||
- name: Lint with black
|
||||
run: pipenv run black --check passbook
|
||||
prospector:
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
|
|
5
Pipfile
5
Pipfile
|
@ -51,8 +51,11 @@ bumpversion = "*"
|
|||
colorama = "*"
|
||||
coverage = "*"
|
||||
django-debug-toolbar = "*"
|
||||
isort = "*"
|
||||
prospector = "*"
|
||||
pylint = "*"
|
||||
pylint-django = "*"
|
||||
unittest-xml-reporting = "*"
|
||||
black = "*"
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
||||
|
|
126
Pipfile.lock
generated
126
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "5d1d5f5f9664ce6ffb10e89d3780c9e04d4f8f372129baaf3293e44432a2f16d"
|
||||
"sha256": "138816efaba5be0b175cfd5b5e6a0b58e5ba551567f0efb441740344da3986d8"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -238,11 +238,11 @@
|
|||
},
|
||||
"django-prometheus": {
|
||||
"hashes": [
|
||||
"sha256:60f331788f9846891e9ea8d7ccd2928b1042e2e99c8d673f97e2b85f5bc20112",
|
||||
"sha256:bb2d4f8acd681fa5787df77e7482391017f0090c70473bccd2aa7cad327800ad"
|
||||
"sha256:f0657d4b887309086b71b55f6aa4a95f967b35fe115128b501f95422c423b12c",
|
||||
"sha256:f645016ae5270ac2025a70788cd2bd636244a0c5705b323cc086994bf828181e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.1.0"
|
||||
"version": "==2.0.0.dev124"
|
||||
},
|
||||
"django-recaptcha": {
|
||||
"hashes": [
|
||||
|
@ -685,20 +685,20 @@
|
|||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc",
|
||||
"sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803",
|
||||
"sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc",
|
||||
"sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15",
|
||||
"sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075",
|
||||
"sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd",
|
||||
"sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31",
|
||||
"sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f",
|
||||
"sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c",
|
||||
"sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04",
|
||||
"sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"
|
||||
"sha256:21a8e19e2007a4047ffabbd8f0ee32c0dabae3b7f4b6c645110ae53e7714b470",
|
||||
"sha256:74ad685bfb065f4bdd36d24aa97092f04bcbb1179b5ffdd3d5f994023fb8c292",
|
||||
"sha256:79c3ba1da22e61c2a71aaa382c57518ab492278c8974c40187b900b50f3e0282",
|
||||
"sha256:94ad913ab3fd967d14ecffda8182d7d0e1f7dd919b352773c492ec51890d3224",
|
||||
"sha256:998db501e3a627c3e5678d6505f0e182d1529545df289db036cdc717f35d8058",
|
||||
"sha256:9b69d4645bff5820713e8912bc61c4277dc127a6f8c197b52b6436503c42600f",
|
||||
"sha256:9da13b536533518343a04f3c6564782ec8a13c705310b26b4832d77fa4d92a47",
|
||||
"sha256:a76159f13b47fb44fb2acac8fef798a1940dd31b4acec6f4560bd11b2d92d31b",
|
||||
"sha256:a9e9175c1e47a089a2b45d9e2afc6aae1f1f725538c32eec761894a42ba1227f",
|
||||
"sha256:ea51ce7b96646ecd3bb12c2702e570c2bd7dd4d9f146db7fa83c5008ede35f66",
|
||||
"sha256:ffbaaa05de60fc444eda3f6300d1af27d965b09b67f1fb4ebcc88dd0fb4ab1b4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.2"
|
||||
"version": "==5.3b1"
|
||||
},
|
||||
"qrcode": {
|
||||
"hashes": [
|
||||
|
@ -858,6 +858,13 @@
|
|||
}
|
||||
},
|
||||
"develop": {
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
|
||||
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
|
||||
],
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"asgiref": {
|
||||
"hashes": [
|
||||
"sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0",
|
||||
|
@ -872,6 +879,13 @@
|
|||
],
|
||||
"version": "==2.3.3"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
|
||||
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
|
||||
],
|
||||
"version": "==19.3.0"
|
||||
},
|
||||
"autopep8": {
|
||||
"hashes": [
|
||||
"sha256:4d8eec30cc81bc5617dbf1218201d770dc35629363547f17577c61683ccfb3ee"
|
||||
|
@ -887,6 +901,14 @@
|
|||
"index": "pypi",
|
||||
"version": "==1.6.2"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b",
|
||||
"sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==19.10b0"
|
||||
},
|
||||
"bumpversion": {
|
||||
"hashes": [
|
||||
"sha256:6744c873dd7aafc24453d8b6a1a0d6d109faf63cd0cd19cb78fd46e74932c77e",
|
||||
|
@ -895,6 +917,13 @@
|
|||
"index": "pypi",
|
||||
"version": "==0.5.3"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
||||
],
|
||||
"version": "==7.0"
|
||||
},
|
||||
"colorama": {
|
||||
"hashes": [
|
||||
"sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff",
|
||||
|
@ -981,7 +1010,6 @@
|
|||
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
|
||||
"sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.21"
|
||||
},
|
||||
"lazy-object-proxy": {
|
||||
|
@ -1017,6 +1045,13 @@
|
|||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424",
|
||||
"sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"
|
||||
],
|
||||
"version": "==0.7.0"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
"sha256:139d2625547dbfa5fb0b81daebb39601c478c21956dc57e2e07b74450a8c506b",
|
||||
|
@ -1103,20 +1138,46 @@
|
|||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc",
|
||||
"sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803",
|
||||
"sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc",
|
||||
"sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15",
|
||||
"sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075",
|
||||
"sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd",
|
||||
"sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31",
|
||||
"sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f",
|
||||
"sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c",
|
||||
"sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04",
|
||||
"sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"
|
||||
"sha256:21a8e19e2007a4047ffabbd8f0ee32c0dabae3b7f4b6c645110ae53e7714b470",
|
||||
"sha256:74ad685bfb065f4bdd36d24aa97092f04bcbb1179b5ffdd3d5f994023fb8c292",
|
||||
"sha256:79c3ba1da22e61c2a71aaa382c57518ab492278c8974c40187b900b50f3e0282",
|
||||
"sha256:94ad913ab3fd967d14ecffda8182d7d0e1f7dd919b352773c492ec51890d3224",
|
||||
"sha256:998db501e3a627c3e5678d6505f0e182d1529545df289db036cdc717f35d8058",
|
||||
"sha256:9b69d4645bff5820713e8912bc61c4277dc127a6f8c197b52b6436503c42600f",
|
||||
"sha256:9da13b536533518343a04f3c6564782ec8a13c705310b26b4832d77fa4d92a47",
|
||||
"sha256:a76159f13b47fb44fb2acac8fef798a1940dd31b4acec6f4560bd11b2d92d31b",
|
||||
"sha256:a9e9175c1e47a089a2b45d9e2afc6aae1f1f725538c32eec761894a42ba1227f",
|
||||
"sha256:ea51ce7b96646ecd3bb12c2702e570c2bd7dd4d9f146db7fa83c5008ede35f66",
|
||||
"sha256:ffbaaa05de60fc444eda3f6300d1af27d965b09b67f1fb4ebcc88dd0fb4ab1b4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.2"
|
||||
"version": "==5.3b1"
|
||||
},
|
||||
"regex": {
|
||||
"hashes": [
|
||||
"sha256:032fdcc03406e1a6485ec09b826eac78732943840c4b29e503b789716f051d8d",
|
||||
"sha256:0e6cf1e747f383f52a0964452658c04300a9a01e8a89c55ea22813931b580aa8",
|
||||
"sha256:106e25a841921d8259dcef2a42786caae35bc750fb996f830065b3dfaa67b77e",
|
||||
"sha256:1768cf42a78a11dae63152685e7a1d90af7a8d71d2d4f6d2387edea53a9e0588",
|
||||
"sha256:27d1bd20d334f50b7ef078eba0f0756a640fd25f5f1708d3b5bed18a5d6bced9",
|
||||
"sha256:29b20f66f2e044aafba86ecf10a84e611b4667643c42baa004247f5dfef4f90b",
|
||||
"sha256:4850c78b53acf664a6578bba0e9ebeaf2807bb476c14ec7e0f936f2015133cae",
|
||||
"sha256:57eacd38a5ec40ed7b19a968a9d01c0d977bda55664210be713e750dd7b33540",
|
||||
"sha256:724eb24b92fc5fdc1501a1b4df44a68b9c1dda171c8ef8736799e903fb100f63",
|
||||
"sha256:77ae8d926f38700432807ba293d768ba9e7652df0cbe76df2843b12f80f68885",
|
||||
"sha256:78b3712ec529b2a71731fbb10b907b54d9c53a17ca589b42a578bc1e9a2c82ea",
|
||||
"sha256:7bbbdbada3078dc360d4692a9b28479f569db7fc7f304b668787afc9feb38ec8",
|
||||
"sha256:8d9ef7f6c403e35e73b7fc3cde9f6decdc43b1cb2ff8d058c53b9084bfcb553e",
|
||||
"sha256:a83049eb717ae828ced9cf607845929efcb086a001fc8af93ff15c50012a5716",
|
||||
"sha256:adc35d38952e688535980ae2109cad3a109520033642e759f987cf47fe278aa1",
|
||||
"sha256:c29a77ad4463f71a506515d9ec3a899ed026b4b015bf43245c919ff36275444b",
|
||||
"sha256:cfd31b3300fefa5eecb2fe596c6dee1b91b3a05ece9d5cfd2631afebf6c6fadd",
|
||||
"sha256:d3ee0b035816e0520fac928de31b6572106f0d75597f6fa3206969a02baba06f",
|
||||
"sha256:d508875793efdf6bab3d47850df8f40d4040ae9928d9d80864c1768d6aeaf8e3",
|
||||
"sha256:ef0b828a7e22e58e06a1cceddba7b4665c6af8afeb22a0d8083001330572c147",
|
||||
"sha256:faad39fdbe2c2ccda9846cd21581063086330efafa47d87afea4073a08128656"
|
||||
],
|
||||
"version": "==2019.12.20"
|
||||
},
|
||||
"requirements-detector": {
|
||||
"hashes": [
|
||||
|
@ -1165,6 +1226,13 @@
|
|||
],
|
||||
"version": "==1.31.0"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
||||
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
|
||||
],
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161",
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
"""passbook"""
|
||||
__version__ = '0.7.5-beta'
|
||||
__version__ = "0.7.5-beta"
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.apps import AppConfig
|
|||
class PassbookAdminConfig(AppConfig):
|
||||
"""passbook admin app config"""
|
||||
|
||||
name = 'passbook.admin'
|
||||
label = 'passbook_admin'
|
||||
mountpoint = 'administration/'
|
||||
verbose_name = 'passbook Admin'
|
||||
name = "passbook.admin"
|
||||
label = "passbook_admin"
|
||||
mountpoint = "administration/"
|
||||
verbose_name = "passbook Admin"
|
||||
|
|
|
@ -16,7 +16,7 @@ class YAMLField(forms.CharField):
|
|||
"""Django's JSON Field converted to YAML"""
|
||||
|
||||
default_error_messages = {
|
||||
'invalid': _("'%(value)s' value must be valid YAML."),
|
||||
"invalid": _("'%(value)s' value must be valid YAML."),
|
||||
}
|
||||
widget = forms.Textarea
|
||||
|
||||
|
@ -31,9 +31,7 @@ class YAMLField(forms.CharField):
|
|||
converted = yaml.safe_load(value)
|
||||
except yaml.YAMLError:
|
||||
raise forms.ValidationError(
|
||||
self.error_messages['invalid'],
|
||||
code='invalid',
|
||||
params={'value': value},
|
||||
self.error_messages["invalid"], code="invalid", params={"value": value},
|
||||
)
|
||||
if isinstance(converted, str):
|
||||
return YAMLString(converted)
|
||||
|
|
|
@ -9,29 +9,32 @@ class TagModelForm(forms.ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Check if we have an instance, load tags otherwise use an empty dict
|
||||
instance = kwargs.get('instance', None)
|
||||
instance = kwargs.get("instance", None)
|
||||
tags = instance.tags if instance else {}
|
||||
# Make sure all predefined tags exist in tags, and set default if they don't
|
||||
predefined_tags = self._meta.model().get_predefined_tags() # pylint: disable=no-member
|
||||
predefined_tags = (
|
||||
self._meta.model().get_predefined_tags()
|
||||
) # pylint: disable=no-member
|
||||
for key, value in predefined_tags.items():
|
||||
if key not in tags:
|
||||
tags[key] = value
|
||||
# Format JSON
|
||||
kwargs['initial']['tags'] = tags
|
||||
kwargs["initial"]["tags"] = tags
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def clean_tags(self):
|
||||
"""Make sure all required tags are set"""
|
||||
if hasattr(self.instance, 'get_required_keys') and hasattr(self.instance, 'tags'):
|
||||
if hasattr(self.instance, "get_required_keys") and hasattr(
|
||||
self.instance, "tags"
|
||||
):
|
||||
for key in self.instance.get_required_keys():
|
||||
if key not in self.cleaned_data.get('tags'):
|
||||
if key not in self.cleaned_data.get("tags"):
|
||||
raise forms.ValidationError("Tag %s missing." % key)
|
||||
return self.cleaned_data.get('tags')
|
||||
return self.cleaned_data.get("tags")
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class TagModelFormMeta:
|
||||
"""Base Meta class that uses the YAMLField"""
|
||||
|
||||
field_classes = {
|
||||
'tags': YAMLField
|
||||
}
|
||||
field_classes = {"tags": YAMLField}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""passbook core source form fields"""
|
||||
# from django import forms
|
||||
|
||||
SOURCE_FORM_FIELDS = ['name', 'slug', 'enabled', 'policies']
|
||||
SOURCE_SERIALIZER_FIELDS = ['pk', 'name', 'slug', 'enabled', 'policies']
|
||||
SOURCE_FORM_FIELDS = ["name", "slug", "enabled", "policies"]
|
||||
SOURCE_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled", "policies"]
|
||||
|
||||
# class SourceForm(forms.Form)
|
||||
|
|
|
@ -12,10 +12,10 @@ class UserForm(forms.ModelForm):
|
|||
class Meta:
|
||||
|
||||
model = User
|
||||
fields = ['username', 'name', 'email', 'is_staff', 'is_active', 'attributes']
|
||||
fields = ["username", "name", "email", "is_staff", "is_active", "attributes"]
|
||||
widgets = {
|
||||
'name': forms.TextInput,
|
||||
"name": forms.TextInput,
|
||||
}
|
||||
field_classes = {
|
||||
'attributes': YAMLField,
|
||||
"attributes": YAMLField,
|
||||
}
|
||||
|
|
|
@ -11,15 +11,16 @@ def impersonate(get_response):
|
|||
|
||||
# User is superuser and has __impersonate ID set
|
||||
if request.user.is_superuser and "__impersonate" in request.GET:
|
||||
request.session['impersonate_id'] = request.GET["__impersonate"]
|
||||
request.session["impersonate_id"] = request.GET["__impersonate"]
|
||||
# user wants to stop impersonation
|
||||
elif "__unimpersonate" in request.GET and 'impersonate_id' in request.session:
|
||||
del request.session['impersonate_id']
|
||||
elif "__unimpersonate" in request.GET and "impersonate_id" in request.session:
|
||||
del request.session["impersonate_id"]
|
||||
|
||||
# Actually impersonate user
|
||||
if request.user.is_superuser and 'impersonate_id' in request.session:
|
||||
request.user = User.objects.get(pk=request.session['impersonate_id'])
|
||||
if request.user.is_superuser and "impersonate_id" in request.session:
|
||||
request.user = User.objects.get(pk=request.session["impersonate_id"])
|
||||
|
||||
response = get_response(request)
|
||||
return response
|
||||
|
||||
return middleware
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""passbook admin settings"""
|
||||
|
||||
MIDDLEWARE = [
|
||||
'passbook.admin.middleware.impersonate',
|
||||
"passbook.admin.middleware.impersonate",
|
||||
]
|
||||
|
|
|
@ -10,10 +10,11 @@ from passbook.lib.utils.template import render_to_string
|
|||
register = template.Library()
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def get_links(model_instance):
|
||||
"""Find all link_ methods on an object instance, run them and return as dict"""
|
||||
prefix = 'link_'
|
||||
prefix = "link_"
|
||||
links = {}
|
||||
|
||||
if not isinstance(model_instance, Model):
|
||||
|
@ -21,9 +22,11 @@ def get_links(model_instance):
|
|||
return links
|
||||
|
||||
try:
|
||||
for name, method in inspect.getmembers(model_instance, predicate=inspect.ismethod):
|
||||
for name, method in inspect.getmembers(
|
||||
model_instance, predicate=inspect.ismethod
|
||||
):
|
||||
if name.startswith(prefix):
|
||||
human_name = name.replace(prefix, '').replace('_', ' ').capitalize()
|
||||
human_name = name.replace(prefix, "").replace("_", " ").capitalize()
|
||||
link = method()
|
||||
if link:
|
||||
links[human_name] = link
|
||||
|
@ -36,7 +39,7 @@ def get_links(model_instance):
|
|||
@register.simple_tag(takes_context=True)
|
||||
def get_htmls(context, model_instance):
|
||||
"""Find all html_ methods on an object instance, run them and return as dict"""
|
||||
prefix = 'html_'
|
||||
prefix = "html_"
|
||||
htmls = []
|
||||
|
||||
if not isinstance(model_instance, Model):
|
||||
|
@ -44,9 +47,11 @@ def get_htmls(context, model_instance):
|
|||
return htmls
|
||||
|
||||
try:
|
||||
for name, method in inspect.getmembers(model_instance, predicate=inspect.ismethod):
|
||||
for name, method in inspect.getmembers(
|
||||
model_instance, predicate=inspect.ismethod
|
||||
):
|
||||
if name.startswith(prefix):
|
||||
template, _context = method(context.get('request'))
|
||||
template, _context = method(context.get("request"))
|
||||
htmls.append(render_to_string(template, _context))
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
|
|
@ -1,82 +1,157 @@
|
|||
"""passbook URL Configuration"""
|
||||
from django.urls import path
|
||||
|
||||
from passbook.admin.views import (applications, audit, debug, factors, groups,
|
||||
invitations, overview, policy,
|
||||
property_mapping, providers, sources, users)
|
||||
from passbook.admin.views import (
|
||||
applications,
|
||||
audit,
|
||||
debug,
|
||||
factors,
|
||||
groups,
|
||||
invitations,
|
||||
overview,
|
||||
policy,
|
||||
property_mapping,
|
||||
providers,
|
||||
sources,
|
||||
users,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path('', overview.AdministrationOverviewView.as_view(), name='overview'),
|
||||
path("", overview.AdministrationOverviewView.as_view(), name="overview"),
|
||||
# Applications
|
||||
path('applications/', applications.ApplicationListView.as_view(),
|
||||
name='applications'),
|
||||
path('applications/create/', applications.ApplicationCreateView.as_view(),
|
||||
name='application-create'),
|
||||
path('applications/<uuid:pk>/update/',
|
||||
applications.ApplicationUpdateView.as_view(), name='application-update'),
|
||||
path('applications/<uuid:pk>/delete/',
|
||||
applications.ApplicationDeleteView.as_view(), name='application-delete'),
|
||||
path(
|
||||
"applications/", applications.ApplicationListView.as_view(), name="applications"
|
||||
),
|
||||
path(
|
||||
"applications/create/",
|
||||
applications.ApplicationCreateView.as_view(),
|
||||
name="application-create",
|
||||
),
|
||||
path(
|
||||
"applications/<uuid:pk>/update/",
|
||||
applications.ApplicationUpdateView.as_view(),
|
||||
name="application-update",
|
||||
),
|
||||
path(
|
||||
"applications/<uuid:pk>/delete/",
|
||||
applications.ApplicationDeleteView.as_view(),
|
||||
name="application-delete",
|
||||
),
|
||||
# Sources
|
||||
path('sources/', sources.SourceListView.as_view(), name='sources'),
|
||||
path('sources/create/', sources.SourceCreateView.as_view(), name='source-create'),
|
||||
path('sources/<uuid:pk>/update/', sources.SourceUpdateView.as_view(), name='source-update'),
|
||||
path('sources/<uuid:pk>/delete/', sources.SourceDeleteView.as_view(), name='source-delete'),
|
||||
path("sources/", sources.SourceListView.as_view(), name="sources"),
|
||||
path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
|
||||
path(
|
||||
"sources/<uuid:pk>/update/",
|
||||
sources.SourceUpdateView.as_view(),
|
||||
name="source-update",
|
||||
),
|
||||
path(
|
||||
"sources/<uuid:pk>/delete/",
|
||||
sources.SourceDeleteView.as_view(),
|
||||
name="source-delete",
|
||||
),
|
||||
# Policies
|
||||
path('policies/', policy.PolicyListView.as_view(), name='policies'),
|
||||
path('policies/create/', policy.PolicyCreateView.as_view(), name='policy-create'),
|
||||
path('policies/<uuid:pk>/update/', policy.PolicyUpdateView.as_view(), name='policy-update'),
|
||||
path('policies/<uuid:pk>/delete/', policy.PolicyDeleteView.as_view(), name='policy-delete'),
|
||||
path('policies/<uuid:pk>/test/', policy.PolicyTestView.as_view(), name='policy-test'),
|
||||
path("policies/", policy.PolicyListView.as_view(), name="policies"),
|
||||
path("policies/create/", policy.PolicyCreateView.as_view(), name="policy-create"),
|
||||
path(
|
||||
"policies/<uuid:pk>/update/",
|
||||
policy.PolicyUpdateView.as_view(),
|
||||
name="policy-update",
|
||||
),
|
||||
path(
|
||||
"policies/<uuid:pk>/delete/",
|
||||
policy.PolicyDeleteView.as_view(),
|
||||
name="policy-delete",
|
||||
),
|
||||
path(
|
||||
"policies/<uuid:pk>/test/", policy.PolicyTestView.as_view(), name="policy-test"
|
||||
),
|
||||
# Providers
|
||||
path('providers/', providers.ProviderListView.as_view(), name='providers'),
|
||||
path('providers/create/',
|
||||
providers.ProviderCreateView.as_view(), name='provider-create'),
|
||||
path('providers/<int:pk>/update/',
|
||||
providers.ProviderUpdateView.as_view(), name='provider-update'),
|
||||
path('providers/<int:pk>/delete/',
|
||||
providers.ProviderDeleteView.as_view(), name='provider-delete'),
|
||||
path("providers/", providers.ProviderListView.as_view(), name="providers"),
|
||||
path(
|
||||
"providers/create/",
|
||||
providers.ProviderCreateView.as_view(),
|
||||
name="provider-create",
|
||||
),
|
||||
path(
|
||||
"providers/<int:pk>/update/",
|
||||
providers.ProviderUpdateView.as_view(),
|
||||
name="provider-update",
|
||||
),
|
||||
path(
|
||||
"providers/<int:pk>/delete/",
|
||||
providers.ProviderDeleteView.as_view(),
|
||||
name="provider-delete",
|
||||
),
|
||||
# Factors
|
||||
path('factors/', factors.FactorListView.as_view(), name='factors'),
|
||||
path('factors/create/',
|
||||
factors.FactorCreateView.as_view(), name='factor-create'),
|
||||
path('factors/<uuid:pk>/update/',
|
||||
factors.FactorUpdateView.as_view(), name='factor-update'),
|
||||
path('factors/<uuid:pk>/delete/',
|
||||
factors.FactorDeleteView.as_view(), name='factor-delete'),
|
||||
path("factors/", factors.FactorListView.as_view(), name="factors"),
|
||||
path("factors/create/", factors.FactorCreateView.as_view(), name="factor-create"),
|
||||
path(
|
||||
"factors/<uuid:pk>/update/",
|
||||
factors.FactorUpdateView.as_view(),
|
||||
name="factor-update",
|
||||
),
|
||||
path(
|
||||
"factors/<uuid:pk>/delete/",
|
||||
factors.FactorDeleteView.as_view(),
|
||||
name="factor-delete",
|
||||
),
|
||||
# Factors
|
||||
path('property-mappings/', property_mapping.PropertyMappingListView.as_view(),
|
||||
name='property-mappings'),
|
||||
path('property-mappings/create/',
|
||||
property_mapping.PropertyMappingCreateView.as_view(), name='property-mapping-create'),
|
||||
path('property-mappings/<uuid:pk>/update/',
|
||||
property_mapping.PropertyMappingUpdateView.as_view(), name='property-mapping-update'),
|
||||
path('property-mappings/<uuid:pk>/delete/',
|
||||
property_mapping.PropertyMappingDeleteView.as_view(), name='property-mapping-delete'),
|
||||
path(
|
||||
"property-mappings/",
|
||||
property_mapping.PropertyMappingListView.as_view(),
|
||||
name="property-mappings",
|
||||
),
|
||||
path(
|
||||
"property-mappings/create/",
|
||||
property_mapping.PropertyMappingCreateView.as_view(),
|
||||
name="property-mapping-create",
|
||||
),
|
||||
path(
|
||||
"property-mappings/<uuid:pk>/update/",
|
||||
property_mapping.PropertyMappingUpdateView.as_view(),
|
||||
name="property-mapping-update",
|
||||
),
|
||||
path(
|
||||
"property-mappings/<uuid:pk>/delete/",
|
||||
property_mapping.PropertyMappingDeleteView.as_view(),
|
||||
name="property-mapping-delete",
|
||||
),
|
||||
# Invitations
|
||||
path('invitations/', invitations.InvitationListView.as_view(), name='invitations'),
|
||||
path('invitations/create/',
|
||||
invitations.InvitationCreateView.as_view(), name='invitation-create'),
|
||||
path('invitations/<uuid:pk>/delete/',
|
||||
invitations.InvitationDeleteView.as_view(), name='invitation-delete'),
|
||||
path("invitations/", invitations.InvitationListView.as_view(), name="invitations"),
|
||||
path(
|
||||
"invitations/create/",
|
||||
invitations.InvitationCreateView.as_view(),
|
||||
name="invitation-create",
|
||||
),
|
||||
path(
|
||||
"invitations/<uuid:pk>/delete/",
|
||||
invitations.InvitationDeleteView.as_view(),
|
||||
name="invitation-delete",
|
||||
),
|
||||
# Users
|
||||
path('users/', users.UserListView.as_view(),
|
||||
name='users'),
|
||||
path('users/create/', users.UserCreateView.as_view(), name='user-create'),
|
||||
path('users/<int:pk>/update/',
|
||||
users.UserUpdateView.as_view(), name='user-update'),
|
||||
path('users/<int:pk>/delete/',
|
||||
users.UserDeleteView.as_view(), name='user-delete'),
|
||||
path('users/<int:pk>/reset/',
|
||||
users.UserPasswordResetView.as_view(), name='user-password-reset'),
|
||||
path("users/", users.UserListView.as_view(), name="users"),
|
||||
path("users/create/", users.UserCreateView.as_view(), name="user-create"),
|
||||
path("users/<int:pk>/update/", users.UserUpdateView.as_view(), name="user-update"),
|
||||
path("users/<int:pk>/delete/", users.UserDeleteView.as_view(), name="user-delete"),
|
||||
path(
|
||||
"users/<int:pk>/reset/",
|
||||
users.UserPasswordResetView.as_view(),
|
||||
name="user-password-reset",
|
||||
),
|
||||
# Groups
|
||||
path('group/', groups.GroupListView.as_view(), name='group'),
|
||||
path('group/create/', groups.GroupCreateView.as_view(), name='group-create'),
|
||||
path('group/<uuid:pk>/update/', groups.GroupUpdateView.as_view(), name='group-update'),
|
||||
path('group/<uuid:pk>/delete/', groups.GroupDeleteView.as_view(), name='group-delete'),
|
||||
path("group/", groups.GroupListView.as_view(), name="group"),
|
||||
path("group/create/", groups.GroupCreateView.as_view(), name="group-create"),
|
||||
path(
|
||||
"group/<uuid:pk>/update/", groups.GroupUpdateView.as_view(), name="group-update"
|
||||
),
|
||||
path(
|
||||
"group/<uuid:pk>/delete/", groups.GroupDeleteView.as_view(), name="group-delete"
|
||||
),
|
||||
# Audit Log
|
||||
path('audit/', audit.EventListView.as_view(), name='audit-log'),
|
||||
path("audit/", audit.EventListView.as_view(), name="audit-log"),
|
||||
# Groups
|
||||
path('groups/', groups.GroupListView.as_view(), name='groups'),
|
||||
path("groups/", groups.GroupListView.as_view(), name="groups"),
|
||||
# Debug
|
||||
path('debug/request/', debug.DebugRequestView.as_view(), name='debug-request'),
|
||||
path("debug/request/", debug.DebugRequestView.as_view(), name="debug-request"),
|
||||
]
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook Application administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
|
@ -18,55 +19,61 @@ class ApplicationListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|||
"""Show list of all applications"""
|
||||
|
||||
model = Application
|
||||
permission_required = 'passbook_core.view_application'
|
||||
ordering = 'name'
|
||||
permission_required = "passbook_core.view_application"
|
||||
ordering = "name"
|
||||
paginate_by = 40
|
||||
template_name = 'administration/application/list.html'
|
||||
template_name = "administration/application/list.html"
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class ApplicationCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class ApplicationCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Application"""
|
||||
|
||||
model = Application
|
||||
form_class = ApplicationForm
|
||||
permission_required = 'passbook_core.add_application'
|
||||
permission_required = "passbook_core.add_application"
|
||||
|
||||
template_name = 'generic/create.html'
|
||||
success_url = reverse_lazy('passbook_admin:applications')
|
||||
success_message = _('Successfully created Application')
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:applications")
|
||||
success_message = _("Successfully created Application")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['type'] = 'Application'
|
||||
kwargs["type"] = "Application"
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class ApplicationUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, UpdateView):
|
||||
class ApplicationUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update application"""
|
||||
|
||||
model = Application
|
||||
form_class = ApplicationForm
|
||||
permission_required = 'passbook_core.change_application'
|
||||
permission_required = "passbook_core.change_application"
|
||||
|
||||
template_name = 'generic/update.html'
|
||||
success_url = reverse_lazy('passbook_admin:applications')
|
||||
success_message = _('Successfully updated Application')
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:applications")
|
||||
success_message = _("Successfully updated Application")
|
||||
|
||||
|
||||
class ApplicationDeleteView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, DeleteView):
|
||||
class ApplicationDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete application"""
|
||||
|
||||
model = Application
|
||||
permission_required = 'passbook_core.delete_application'
|
||||
permission_required = "passbook_core.delete_application"
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
success_url = reverse_lazy('passbook_admin:applications')
|
||||
success_message = _('Successfully deleted Application')
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:applications")
|
||||
success_message = _("Successfully deleted Application")
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
|
|
@ -9,10 +9,10 @@ class EventListView(PermissionListMixin, ListView):
|
|||
"""Show list of all invitations"""
|
||||
|
||||
model = Event
|
||||
template_name = 'administration/audit/list.html'
|
||||
permission_required = 'passbook_audit.view_event'
|
||||
ordering = '-created'
|
||||
template_name = "administration/audit/list.html"
|
||||
permission_required = "passbook_audit.view_event"
|
||||
ordering = "-created"
|
||||
paginate_by = 10
|
||||
|
||||
def get_queryset(self):
|
||||
return Event.objects.all().order_by('-created')
|
||||
return Event.objects.all().order_by("-created")
|
||||
|
|
|
@ -6,10 +6,10 @@ from django.views.generic import TemplateView
|
|||
class DebugRequestView(LoginRequiredMixin, TemplateView):
|
||||
"""Show debug info about request"""
|
||||
|
||||
template_name = 'administration/debug/request.html'
|
||||
template_name = "administration/debug/request.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['request_dict'] = {}
|
||||
kwargs["request_dict"] = {}
|
||||
for key in dir(self.request):
|
||||
kwargs['request_dict'][key] = getattr(self.request, key)
|
||||
kwargs["request_dict"][key] = getattr(self.request, key)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook Factor administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import Http404
|
||||
from django.urls import reverse_lazy
|
||||
|
@ -18,62 +19,69 @@ from passbook.lib.views import CreateAssignPermView
|
|||
def all_subclasses(cls):
|
||||
"""Recursively return all subclassess of cls"""
|
||||
return set(cls.__subclasses__()).union(
|
||||
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
|
||||
[s for c in cls.__subclasses__() for s in all_subclasses(c)]
|
||||
)
|
||||
|
||||
|
||||
class FactorListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
"""Show list of all factors"""
|
||||
|
||||
model = Factor
|
||||
template_name = 'administration/factor/list.html'
|
||||
permission_required = 'passbook_core.view_factor'
|
||||
ordering = 'order'
|
||||
template_name = "administration/factor/list.html"
|
||||
permission_required = "passbook_core.view_factor"
|
||||
ordering = "order"
|
||||
paginate_by = 40
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['types'] = {
|
||||
x.__name__: x._meta.verbose_name for x in all_subclasses(Factor)}
|
||||
kwargs["types"] = {
|
||||
x.__name__: x._meta.verbose_name for x in all_subclasses(Factor)
|
||||
}
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class FactorCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class FactorCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Factor"""
|
||||
|
||||
model = Factor
|
||||
template_name = 'generic/create.html'
|
||||
permission_required = 'passbook_core.add_factor'
|
||||
template_name = "generic/create.html"
|
||||
permission_required = "passbook_core.add_factor"
|
||||
|
||||
success_url = reverse_lazy('passbook_admin:factors')
|
||||
success_message = _('Successfully created Factor')
|
||||
success_url = reverse_lazy("passbook_admin:factors")
|
||||
success_message = _("Successfully created Factor")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
factor_type = self.request.GET.get('type')
|
||||
factor_type = self.request.GET.get("type")
|
||||
model = next(x for x in all_subclasses(Factor) if x.__name__ == factor_type)
|
||||
kwargs['type'] = model._meta.verbose_name
|
||||
kwargs["type"] = model._meta.verbose_name
|
||||
return kwargs
|
||||
|
||||
def get_form_class(self):
|
||||
factor_type = self.request.GET.get('type')
|
||||
factor_type = self.request.GET.get("type")
|
||||
model = next(x for x in all_subclasses(Factor) if x.__name__ == factor_type)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
|
||||
|
||||
class FactorUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, UpdateView):
|
||||
class FactorUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update factor"""
|
||||
|
||||
model = Factor
|
||||
permission_required = 'passbook_core.update_application'
|
||||
template_name = 'generic/update.html'
|
||||
success_url = reverse_lazy('passbook_admin:factors')
|
||||
success_message = _('Successfully updated Factor')
|
||||
permission_required = "passbook_core.update_application"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:factors")
|
||||
success_message = _("Successfully updated Factor")
|
||||
|
||||
def get_form_class(self):
|
||||
form_class_path = self.get_object().form
|
||||
|
@ -81,21 +89,26 @@ class FactorUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
|||
return form_class
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Factor.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Factor.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
|
||||
)
|
||||
|
||||
|
||||
class FactorDeleteView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, DeleteView):
|
||||
class FactorDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete factor"""
|
||||
|
||||
model = Factor
|
||||
template_name = 'generic/delete.html'
|
||||
permission_required = 'passbook_core.delete_factor'
|
||||
success_url = reverse_lazy('passbook_admin:factors')
|
||||
success_message = _('Successfully deleted Factor')
|
||||
template_name = "generic/delete.html"
|
||||
permission_required = "passbook_core.delete_factor"
|
||||
success_url = reverse_lazy("passbook_admin:factors")
|
||||
success_message = _("Successfully deleted Factor")
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Factor.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Factor.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
|
||||
)
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook Group administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
|
@ -18,40 +19,45 @@ class GroupListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|||
"""Show list of all groups"""
|
||||
|
||||
model = Group
|
||||
permission_required = 'passbook_core.view_group'
|
||||
ordering = 'name'
|
||||
permission_required = "passbook_core.view_group"
|
||||
ordering = "name"
|
||||
paginate_by = 40
|
||||
template_name = 'administration/group/list.html'
|
||||
template_name = "administration/group/list.html"
|
||||
|
||||
|
||||
class GroupCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class GroupCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Group"""
|
||||
|
||||
model = Group
|
||||
form_class = GroupForm
|
||||
permission_required = 'passbook_core.add_group'
|
||||
permission_required = "passbook_core.add_group"
|
||||
|
||||
template_name = 'generic/create.html'
|
||||
success_url = reverse_lazy('passbook_admin:groups')
|
||||
success_message = _('Successfully created Group')
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:groups")
|
||||
success_message = _("Successfully created Group")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['type'] = 'Group'
|
||||
kwargs["type"] = "Group"
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class GroupUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, UpdateView):
|
||||
class GroupUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update group"""
|
||||
|
||||
model = Group
|
||||
form_class = GroupForm
|
||||
permission_required = 'passbook_core.change_group'
|
||||
permission_required = "passbook_core.change_group"
|
||||
|
||||
template_name = 'generic/update.html'
|
||||
success_url = reverse_lazy('passbook_admin:groups')
|
||||
success_message = _('Successfully updated Group')
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:groups")
|
||||
success_message = _("Successfully updated Group")
|
||||
|
||||
|
||||
class GroupDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
|
||||
|
@ -59,9 +65,9 @@ class GroupDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
|
|||
|
||||
model = Group
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
success_url = reverse_lazy('passbook_admin:groups')
|
||||
success_message = _('Successfully deleted Group')
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:groups")
|
||||
success_message = _("Successfully deleted Group")
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook Invitation administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse_lazy
|
||||
|
@ -20,47 +21,49 @@ class InvitationListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|||
"""Show list of all invitations"""
|
||||
|
||||
model = Invitation
|
||||
permission_required = 'passbook_core.view_invitation'
|
||||
template_name = 'administration/invitation/list.html'
|
||||
permission_required = "passbook_core.view_invitation"
|
||||
template_name = "administration/invitation/list.html"
|
||||
|
||||
|
||||
class InvitationCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class InvitationCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Invitation"""
|
||||
|
||||
model = Invitation
|
||||
form_class = InvitationForm
|
||||
permission_required = 'passbook_core.add_invitation'
|
||||
permission_required = "passbook_core.add_invitation"
|
||||
|
||||
template_name = 'generic/create.html'
|
||||
success_url = reverse_lazy('passbook_admin:invitations')
|
||||
success_message = _('Successfully created Invitation')
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:invitations")
|
||||
success_message = _("Successfully created Invitation")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['type'] = 'Invitation'
|
||||
kwargs["type"] = "Invitation"
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.save()
|
||||
invitation_created.send(
|
||||
sender=self,
|
||||
request=self.request,
|
||||
invitation=obj)
|
||||
invitation_created.send(sender=self, request=self.request, invitation=obj)
|
||||
return HttpResponseRedirect(self.success_url)
|
||||
|
||||
|
||||
class InvitationDeleteView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, DeleteView):
|
||||
class InvitationDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete invitation"""
|
||||
|
||||
model = Invitation
|
||||
permission_required = 'passbook_core.delete_invitation'
|
||||
permission_required = "passbook_core.delete_invitation"
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
success_url = reverse_lazy('passbook_admin:invitations')
|
||||
success_message = _('Successfully deleted Invitation')
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:invitations")
|
||||
success_message = _("Successfully deleted Invitation")
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
|
|
@ -5,34 +5,45 @@ from django.views.generic import TemplateView
|
|||
|
||||
from passbook import __version__
|
||||
from passbook.admin.mixins import AdminRequiredMixin
|
||||
from passbook.core.models import (Application, Factor, Invitation, Policy,
|
||||
Provider, Source, User)
|
||||
from passbook.core.models import (
|
||||
Application,
|
||||
Factor,
|
||||
Invitation,
|
||||
Policy,
|
||||
Provider,
|
||||
Source,
|
||||
User,
|
||||
)
|
||||
from passbook.root.celery import CELERY_APP
|
||||
|
||||
|
||||
class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
|
||||
"""Overview View"""
|
||||
|
||||
template_name = 'administration/overview.html'
|
||||
template_name = "administration/overview.html"
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
"""Handle post (clear cache from modal)"""
|
||||
if 'clear' in self.request.POST:
|
||||
if "clear" in self.request.POST:
|
||||
cache.clear()
|
||||
return redirect(reverse('passbook_core:auth-login'))
|
||||
return redirect(reverse("passbook_core:auth-login"))
|
||||
return self.get(*args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['application_count'] = len(Application.objects.all())
|
||||
kwargs['policy_count'] = len(Policy.objects.all())
|
||||
kwargs['user_count'] = len(User.objects.all())
|
||||
kwargs['provider_count'] = len(Provider.objects.all())
|
||||
kwargs['source_count'] = len(Source.objects.all())
|
||||
kwargs['factor_count'] = len(Factor.objects.all())
|
||||
kwargs['invitation_count'] = len(Invitation.objects.all())
|
||||
kwargs['version'] = __version__
|
||||
kwargs['worker_count'] = len(CELERY_APP.control.ping(timeout=0.5))
|
||||
kwargs['providers_without_application'] = Provider.objects.filter(application=None)
|
||||
kwargs['policies_without_attachment'] = len(Policy.objects.filter(policymodel__isnull=True))
|
||||
kwargs['cached_policies'] = len(cache.keys('policy_*'))
|
||||
kwargs["application_count"] = len(Application.objects.all())
|
||||
kwargs["policy_count"] = len(Policy.objects.all())
|
||||
kwargs["user_count"] = len(User.objects.all())
|
||||
kwargs["provider_count"] = len(Provider.objects.all())
|
||||
kwargs["source_count"] = len(Source.objects.all())
|
||||
kwargs["factor_count"] = len(Factor.objects.all())
|
||||
kwargs["invitation_count"] = len(Invitation.objects.all())
|
||||
kwargs["version"] = __version__
|
||||
kwargs["worker_count"] = len(CELERY_APP.control.ping(timeout=0.5))
|
||||
kwargs["providers_without_application"] = Provider.objects.filter(
|
||||
application=None
|
||||
)
|
||||
kwargs["policies_without_attachment"] = len(
|
||||
Policy.objects.filter(policymodel__isnull=True)
|
||||
)
|
||||
kwargs["cached_policies"] = len(cache.keys("policy_*"))
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook Policy administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import Http404
|
||||
from django.urls import reverse_lazy
|
||||
|
@ -22,49 +23,54 @@ class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|||
"""Show list of all policies"""
|
||||
|
||||
model = Policy
|
||||
permission_required = 'passbook_core.view_policy'
|
||||
permission_required = "passbook_core.view_policy"
|
||||
|
||||
template_name = 'administration/policy/list.html'
|
||||
template_name = "administration/policy/list.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['types'] = {
|
||||
x.__name__: x._meta.verbose_name for x in Policy.__subclasses__()}
|
||||
kwargs["types"] = {
|
||||
x.__name__: x._meta.verbose_name for x in Policy.__subclasses__()
|
||||
}
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().order_by('order').select_subclasses()
|
||||
return super().get_queryset().order_by("order").select_subclasses()
|
||||
|
||||
|
||||
class PolicyCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class PolicyCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Policy"""
|
||||
|
||||
model = Policy
|
||||
permission_required = 'passbook_core.add_policy'
|
||||
permission_required = "passbook_core.add_policy"
|
||||
|
||||
template_name = 'generic/create.html'
|
||||
success_url = reverse_lazy('passbook_admin:policies')
|
||||
success_message = _('Successfully created Policy')
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:policies")
|
||||
success_message = _("Successfully created Policy")
|
||||
|
||||
def get_form_class(self):
|
||||
policy_type = self.request.GET.get('type')
|
||||
model = next(x for x in Policy.__subclasses__()
|
||||
if x.__name__ == policy_type)
|
||||
policy_type = self.request.GET.get("type")
|
||||
model = next(x for x in Policy.__subclasses__() if x.__name__ == policy_type)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
|
||||
|
||||
class PolicyUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, UpdateView):
|
||||
class PolicyUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update policy"""
|
||||
|
||||
model = Policy
|
||||
permission_required = 'passbook_core.change_policy'
|
||||
permission_required = "passbook_core.change_policy"
|
||||
|
||||
template_name = 'generic/update.html'
|
||||
success_url = reverse_lazy('passbook_admin:policies')
|
||||
success_message = _('Successfully updated Policy')
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:policies")
|
||||
success_message = _("Successfully updated Policy")
|
||||
|
||||
def get_form_class(self):
|
||||
form_class_path = self.get_object().form
|
||||
|
@ -72,22 +78,27 @@ class PolicyUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
|||
return form_class
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Policy.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Policy.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
|
||||
)
|
||||
|
||||
|
||||
class PolicyDeleteView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, DeleteView):
|
||||
class PolicyDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete policy"""
|
||||
|
||||
model = Policy
|
||||
permission_required = 'passbook_core.delete_policy'
|
||||
permission_required = "passbook_core.delete_policy"
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
success_url = reverse_lazy('passbook_admin:policies')
|
||||
success_message = _('Successfully deleted Policy')
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:policies")
|
||||
success_message = _("Successfully deleted Policy")
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Policy.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Policy.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
|
||||
)
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
@ -99,15 +110,17 @@ class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, Fo
|
|||
|
||||
model = Policy
|
||||
form_class = PolicyTestForm
|
||||
permission_required = 'passbook_core.view_policy'
|
||||
template_name = 'administration/policy/test.html'
|
||||
permission_required = "passbook_core.view_policy"
|
||||
template_name = "administration/policy/test.html"
|
||||
object = None
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Policy.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Policy.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['policy'] = self.get_object()
|
||||
kwargs["policy"] = self.get_object()
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
|
@ -116,13 +129,13 @@ class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, Fo
|
|||
|
||||
def form_valid(self, form):
|
||||
policy = self.get_object()
|
||||
user = form.cleaned_data.get('user')
|
||||
user = form.cleaned_data.get("user")
|
||||
policy_engine = PolicyEngine([policy], user, self.request)
|
||||
policy_engine.use_cache = False
|
||||
policy_engine.build()
|
||||
result = policy_engine.passing
|
||||
if result:
|
||||
messages.success(self.request, _('User successfully passed policy.'))
|
||||
messages.success(self.request, _("User successfully passed policy."))
|
||||
else:
|
||||
messages.error(self.request, _("User didn't pass policy."))
|
||||
return self.render_to_response(self.get_context_data(form=form, result=result))
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook PropertyMapping administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import Http404
|
||||
from django.urls import reverse_lazy
|
||||
|
@ -18,65 +19,78 @@ from passbook.lib.views import CreateAssignPermView
|
|||
def all_subclasses(cls):
|
||||
"""Recursively return all subclassess of cls"""
|
||||
return set(cls.__subclasses__()).union(
|
||||
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
|
||||
[s for c in cls.__subclasses__() for s in all_subclasses(c)]
|
||||
)
|
||||
|
||||
|
||||
class PropertyMappingListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
"""Show list of all property_mappings"""
|
||||
|
||||
model = PropertyMapping
|
||||
permission_required = 'passbook_core.view_propertymapping'
|
||||
template_name = 'administration/property_mapping/list.html'
|
||||
ordering = 'name'
|
||||
permission_required = "passbook_core.view_propertymapping"
|
||||
template_name = "administration/property_mapping/list.html"
|
||||
ordering = "name"
|
||||
paginate_by = 40
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['types'] = {
|
||||
x.__name__: x._meta.verbose_name for x in all_subclasses(PropertyMapping)}
|
||||
kwargs["types"] = {
|
||||
x.__name__: x._meta.verbose_name for x in all_subclasses(PropertyMapping)
|
||||
}
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class PropertyMappingCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class PropertyMappingCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new PropertyMapping"""
|
||||
|
||||
model = PropertyMapping
|
||||
permission_required = 'passbook_core.add_propertymapping'
|
||||
permission_required = "passbook_core.add_propertymapping"
|
||||
|
||||
template_name = 'generic/create.html'
|
||||
success_url = reverse_lazy('passbook_admin:property-mappings')
|
||||
success_message = _('Successfully created Property Mapping')
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:property-mappings")
|
||||
success_message = _("Successfully created Property Mapping")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
property_mapping_type = self.request.GET.get('type')
|
||||
model = next(x for x in all_subclasses(PropertyMapping)
|
||||
if x.__name__ == property_mapping_type)
|
||||
kwargs['type'] = model._meta.verbose_name
|
||||
property_mapping_type = self.request.GET.get("type")
|
||||
model = next(
|
||||
x
|
||||
for x in all_subclasses(PropertyMapping)
|
||||
if x.__name__ == property_mapping_type
|
||||
)
|
||||
kwargs["type"] = model._meta.verbose_name
|
||||
return kwargs
|
||||
|
||||
def get_form_class(self):
|
||||
property_mapping_type = self.request.GET.get('type')
|
||||
model = next(x for x in all_subclasses(PropertyMapping)
|
||||
if x.__name__ == property_mapping_type)
|
||||
property_mapping_type = self.request.GET.get("type")
|
||||
model = next(
|
||||
x
|
||||
for x in all_subclasses(PropertyMapping)
|
||||
if x.__name__ == property_mapping_type
|
||||
)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
|
||||
|
||||
class PropertyMappingUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, UpdateView):
|
||||
class PropertyMappingUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update property_mapping"""
|
||||
|
||||
model = PropertyMapping
|
||||
permission_required = 'passbook_core.change_propertymapping'
|
||||
permission_required = "passbook_core.change_propertymapping"
|
||||
|
||||
template_name = 'generic/update.html'
|
||||
success_url = reverse_lazy('passbook_admin:property-mappings')
|
||||
success_message = _('Successfully updated Property Mapping')
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:property-mappings")
|
||||
success_message = _("Successfully updated Property Mapping")
|
||||
|
||||
def get_form_class(self):
|
||||
form_class_path = self.get_object().form
|
||||
|
@ -84,22 +98,31 @@ class PropertyMappingUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
|||
return form_class
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return PropertyMapping.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
PropertyMapping.objects.filter(pk=self.kwargs.get("pk"))
|
||||
.select_subclasses()
|
||||
.first()
|
||||
)
|
||||
|
||||
|
||||
class PropertyMappingDeleteView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, DeleteView):
|
||||
class PropertyMappingDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete property_mapping"""
|
||||
|
||||
model = PropertyMapping
|
||||
permission_required = 'passbook_core.delete_propertymapping'
|
||||
permission_required = "passbook_core.delete_propertymapping"
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
success_url = reverse_lazy('passbook_admin:property-mappings')
|
||||
success_message = _('Successfully deleted Property Mapping')
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:property-mappings")
|
||||
success_message = _("Successfully deleted Property Mapping")
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return PropertyMapping.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
PropertyMapping.objects.filter(pk=self.kwargs.get("pk"))
|
||||
.select_subclasses()
|
||||
.first()
|
||||
)
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook Provider administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import Http404
|
||||
from django.urls import reverse_lazy
|
||||
|
@ -19,48 +20,55 @@ class ProviderListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|||
"""Show list of all providers"""
|
||||
|
||||
model = Provider
|
||||
permission_required = 'passbook_core.add_provider'
|
||||
template_name = 'administration/provider/list.html'
|
||||
permission_required = "passbook_core.add_provider"
|
||||
template_name = "administration/provider/list.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['types'] = {
|
||||
x.__name__: x._meta.verbose_name for x in Provider.__subclasses__()}
|
||||
kwargs["types"] = {
|
||||
x.__name__: x._meta.verbose_name for x in Provider.__subclasses__()
|
||||
}
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class ProviderCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class ProviderCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Provider"""
|
||||
|
||||
model = Provider
|
||||
permission_required = 'passbook_core.add_provider'
|
||||
permission_required = "passbook_core.add_provider"
|
||||
|
||||
template_name = 'generic/create.html'
|
||||
success_url = reverse_lazy('passbook_admin:providers')
|
||||
success_message = _('Successfully created Provider')
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:providers")
|
||||
success_message = _("Successfully created Provider")
|
||||
|
||||
def get_form_class(self):
|
||||
provider_type = self.request.GET.get('type')
|
||||
model = next(x for x in Provider.__subclasses__()
|
||||
if x.__name__ == provider_type)
|
||||
provider_type = self.request.GET.get("type")
|
||||
model = next(
|
||||
x for x in Provider.__subclasses__() if x.__name__ == provider_type
|
||||
)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
|
||||
|
||||
class ProviderUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, UpdateView):
|
||||
class ProviderUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update provider"""
|
||||
|
||||
model = Provider
|
||||
permission_required = 'passbook_core.change_provider'
|
||||
permission_required = "passbook_core.change_provider"
|
||||
|
||||
template_name = 'generic/update.html'
|
||||
success_url = reverse_lazy('passbook_admin:providers')
|
||||
success_message = _('Successfully updated Provider')
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:providers")
|
||||
success_message = _("Successfully updated Provider")
|
||||
|
||||
def get_form_class(self):
|
||||
form_class_path = self.get_object().form
|
||||
|
@ -68,22 +76,31 @@ class ProviderUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
|||
return form_class
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Provider.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Provider.objects.filter(pk=self.kwargs.get("pk"))
|
||||
.select_subclasses()
|
||||
.first()
|
||||
)
|
||||
|
||||
|
||||
class ProviderDeleteView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, DeleteView):
|
||||
class ProviderDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete provider"""
|
||||
|
||||
model = Provider
|
||||
permission_required = 'passbook_core.delete_provider'
|
||||
permission_required = "passbook_core.delete_provider"
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
success_url = reverse_lazy('passbook_admin:providers')
|
||||
success_message = _('Successfully deleted Provider')
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:providers")
|
||||
success_message = _("Successfully deleted Provider")
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Provider.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Provider.objects.filter(pk=self.kwargs.get("pk"))
|
||||
.select_subclasses()
|
||||
.first()
|
||||
)
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook Source administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import Http404
|
||||
from django.urls import reverse_lazy
|
||||
|
@ -18,55 +19,63 @@ from passbook.lib.views import CreateAssignPermView
|
|||
def all_subclasses(cls):
|
||||
"""Recursively return all subclassess of cls"""
|
||||
return set(cls.__subclasses__()).union(
|
||||
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
|
||||
[s for c in cls.__subclasses__() for s in all_subclasses(c)]
|
||||
)
|
||||
|
||||
|
||||
class SourceListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
"""Show list of all sources"""
|
||||
|
||||
model = Source
|
||||
permission_required = 'passbook_core.view_source'
|
||||
ordering = 'name'
|
||||
permission_required = "passbook_core.view_source"
|
||||
ordering = "name"
|
||||
paginate_by = 40
|
||||
template_name = 'administration/source/list.html'
|
||||
template_name = "administration/source/list.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['types'] = {
|
||||
x.__name__: x._meta.verbose_name for x in all_subclasses(Source)}
|
||||
kwargs["types"] = {
|
||||
x.__name__: x._meta.verbose_name for x in all_subclasses(Source)
|
||||
}
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class SourceCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class SourceCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Source"""
|
||||
|
||||
model = Source
|
||||
permission_required = 'passbook_core.add_source'
|
||||
permission_required = "passbook_core.add_source"
|
||||
|
||||
template_name = 'generic/create.html'
|
||||
success_url = reverse_lazy('passbook_admin:sources')
|
||||
success_message = _('Successfully created Source')
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:sources")
|
||||
success_message = _("Successfully created Source")
|
||||
|
||||
def get_form_class(self):
|
||||
source_type = self.request.GET.get('type')
|
||||
source_type = self.request.GET.get("type")
|
||||
model = next(x for x in all_subclasses(Source) if x.__name__ == source_type)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
|
||||
|
||||
class SourceUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, UpdateView):
|
||||
class SourceUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update source"""
|
||||
|
||||
model = Source
|
||||
permission_required = 'passbook_core.change_source'
|
||||
permission_required = "passbook_core.change_source"
|
||||
|
||||
template_name = 'generic/update.html'
|
||||
success_url = reverse_lazy('passbook_admin:sources')
|
||||
success_message = _('Successfully updated Source')
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:sources")
|
||||
success_message = _("Successfully updated Source")
|
||||
|
||||
def get_form_class(self):
|
||||
form_class_path = self.get_object().form
|
||||
|
@ -74,22 +83,27 @@ class SourceUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
|||
return form_class
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Source.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Source.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
|
||||
)
|
||||
|
||||
|
||||
class SourceDeleteView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, DeleteView):
|
||||
class SourceDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete source"""
|
||||
|
||||
model = Source
|
||||
permission_required = 'passbook_core.delete_source'
|
||||
permission_required = "passbook_core.delete_source"
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
success_url = reverse_lazy('passbook_admin:sources')
|
||||
success_message = _('Successfully deleted Source')
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:sources")
|
||||
success_message = _("Successfully deleted Source")
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Source.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
return (
|
||||
Source.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
|
||||
)
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""passbook User administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import \
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse, reverse_lazy
|
||||
|
@ -19,50 +20,56 @@ class UserListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|||
"""Show list of all users"""
|
||||
|
||||
model = User
|
||||
permission_required = 'passbook_core.view_user'
|
||||
ordering = 'username'
|
||||
permission_required = "passbook_core.view_user"
|
||||
ordering = "username"
|
||||
paginate_by = 40
|
||||
template_name = 'administration/user/list.html'
|
||||
template_name = "administration/user/list.html"
|
||||
|
||||
|
||||
class UserCreateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin, CreateAssignPermView):
|
||||
class UserCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create user"""
|
||||
|
||||
model = User
|
||||
form_class = UserForm
|
||||
permission_required = 'passbook_core.add_user'
|
||||
permission_required = "passbook_core.add_user"
|
||||
|
||||
template_name = 'generic/create.html'
|
||||
success_url = reverse_lazy('passbook_admin:users')
|
||||
success_message = _('Successfully created User')
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:users")
|
||||
success_message = _("Successfully created User")
|
||||
|
||||
|
||||
class UserUpdateView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, UpdateView):
|
||||
class UserUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update user"""
|
||||
|
||||
model = User
|
||||
form_class = UserForm
|
||||
permission_required = 'passbook_core.change_user'
|
||||
permission_required = "passbook_core.change_user"
|
||||
|
||||
# By default the object's name is user which is used by other checks
|
||||
context_object_name = 'object'
|
||||
template_name = 'generic/update.html'
|
||||
success_url = reverse_lazy('passbook_admin:users')
|
||||
success_message = _('Successfully updated User')
|
||||
context_object_name = "object"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:users")
|
||||
success_message = _("Successfully updated User")
|
||||
|
||||
|
||||
class UserDeleteView(SuccessMessageMixin, LoginRequiredMixin,
|
||||
PermissionRequiredMixin, DeleteView):
|
||||
class UserDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete user"""
|
||||
|
||||
model = User
|
||||
permission_required = 'passbook_core.delete_user'
|
||||
permission_required = "passbook_core.delete_user"
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
success_url = reverse_lazy('passbook_admin:users')
|
||||
success_message = _('Successfully deleted User')
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:users")
|
||||
success_message = _("Successfully deleted User")
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
|
@ -73,13 +80,16 @@ class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
|
|||
"""Get Password reset link for user"""
|
||||
|
||||
model = User
|
||||
permission_required = 'passbook_core.reset_user_password'
|
||||
permission_required = "passbook_core.reset_user_password"
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Create nonce for user and return link"""
|
||||
super().get(request, *args, **kwargs)
|
||||
nonce = Nonce.objects.create(user=self.object)
|
||||
link = request.build_absolute_uri(reverse(
|
||||
'passbook_core:auth-password-reset', kwargs={'nonce': nonce.uuid}))
|
||||
messages.success(request, _('Password reset link: <pre>%(link)s</pre>' % {'link': link}))
|
||||
return redirect('passbook_admin:users')
|
||||
link = request.build_absolute_uri(
|
||||
reverse("passbook_core:auth-password-reset", kwargs={"nonce": nonce.uuid})
|
||||
)
|
||||
messages.success(
|
||||
request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link})
|
||||
)
|
||||
return redirect("passbook_admin:users")
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.apps import AppConfig
|
|||
class PassbookAPIConfig(AppConfig):
|
||||
"""passbook API Config"""
|
||||
|
||||
name = 'passbook.api'
|
||||
label = 'passbook_api'
|
||||
mountpoint = 'api/'
|
||||
verbose_name = 'passbook API'
|
||||
name = "passbook.api"
|
||||
label = "passbook_api"
|
||||
mountpoint = "api/"
|
||||
verbose_name = "passbook API"
|
||||
|
|
|
@ -9,13 +9,13 @@ class CustomObjectPermissions(DjangoObjectPermissions):
|
|||
"""Similar to `DjangoObjectPermissions`, but adding 'view' permissions."""
|
||||
|
||||
perms_map = {
|
||||
'GET': ['%(app_label)s.view_%(model_name)s'],
|
||||
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
|
||||
'HEAD': ['%(app_label)s.view_%(model_name)s'],
|
||||
'POST': ['%(app_label)s.add_%(model_name)s'],
|
||||
'PUT': ['%(app_label)s.change_%(model_name)s'],
|
||||
'PATCH': ['%(app_label)s.change_%(model_name)s'],
|
||||
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
|
||||
"GET": ["%(app_label)s.view_%(model_name)s"],
|
||||
"OPTIONS": ["%(app_label)s.view_%(model_name)s"],
|
||||
"HEAD": ["%(app_label)s.view_%(model_name)s"],
|
||||
"POST": ["%(app_label)s.add_%(model_name)s"],
|
||||
"PUT": ["%(app_label)s.change_%(model_name)s"],
|
||||
"PATCH": ["%(app_label)s.change_%(model_name)s"],
|
||||
"DELETE": ["%(app_label)s.delete_%(model_name)s"],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,6 @@ from passbook.api.v1.urls import urlpatterns as v1_urls
|
|||
from passbook.api.v2.urls import urlpatterns as v2_urls
|
||||
|
||||
urlpatterns = [
|
||||
path('v1/', include(v1_urls)),
|
||||
path('v2/', include(v2_urls)),
|
||||
path("v1/", include(v1_urls)),
|
||||
path("v2/", include(v2_urls)),
|
||||
]
|
||||
|
|
|
@ -7,16 +7,16 @@ from oauth2_provider.views.mixins import ScopedResourceMixin
|
|||
class OpenIDUserInfoView(ScopedResourceMixin, View):
|
||||
"""Passbook v1 OpenID API"""
|
||||
|
||||
required_scopes = ['openid:userinfo']
|
||||
required_scopes = ["openid:userinfo"]
|
||||
|
||||
def get(self, request, *_, **__):
|
||||
"""Passbook v1 OpenID API"""
|
||||
payload = {
|
||||
'sub': request.user.uuid.int,
|
||||
'name': request.user.get_full_name(),
|
||||
'given_name': request.user.name,
|
||||
'family_name': '',
|
||||
'preferred_username': request.user.username,
|
||||
'email': request.user.email,
|
||||
"sub": request.user.uuid.int,
|
||||
"name": request.user.get_full_name(),
|
||||
"given_name": request.user.name,
|
||||
"family_name": "",
|
||||
"preferred_username": request.user.username,
|
||||
"email": request.user.email,
|
||||
}
|
||||
return JsonResponse(payload)
|
||||
|
|
|
@ -3,6 +3,4 @@ from django.urls import path
|
|||
|
||||
from passbook.api.v1.openid import OpenIDUserInfoView
|
||||
|
||||
urlpatterns = [
|
||||
path('openid/', OpenIDUserInfoView.as_view(), name='openid')
|
||||
]
|
||||
urlpatterns = [path("openid/", OpenIDUserInfoView.as_view(), name="openid")]
|
||||
|
|
|
@ -34,70 +34,73 @@ from passbook.policies.webhook.api import WebhookPolicyViewSet
|
|||
from passbook.providers.app_gw.api import ApplicationGatewayProviderViewSet
|
||||
from passbook.providers.oauth.api import OAuth2ProviderViewSet
|
||||
from passbook.providers.oidc.api import OpenIDProviderViewSet
|
||||
from passbook.providers.saml.api import (SAMLPropertyMappingViewSet,
|
||||
SAMLProviderViewSet)
|
||||
from passbook.sources.ldap.api import (LDAPPropertyMappingViewSet,
|
||||
LDAPSourceViewSet)
|
||||
from passbook.providers.saml.api import SAMLPropertyMappingViewSet, SAMLProviderViewSet
|
||||
from passbook.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet
|
||||
from passbook.sources.oauth.api import OAuthSourceViewSet
|
||||
|
||||
LOGGER = get_logger()
|
||||
router = routers.DefaultRouter()
|
||||
|
||||
for _passbook_app in get_apps():
|
||||
if hasattr(_passbook_app, 'api_mountpoint'):
|
||||
if hasattr(_passbook_app, "api_mountpoint"):
|
||||
for prefix, viewset in _passbook_app.api_mountpoint:
|
||||
router.register(prefix, viewset)
|
||||
LOGGER.debug("Mounted API URLs", app_name=_passbook_app.name)
|
||||
|
||||
router.register('core/applications', ApplicationViewSet)
|
||||
router.register('core/invitations', InvitationViewSet)
|
||||
router.register('core/groups', GroupViewSet)
|
||||
router.register('core/users', UserViewSet)
|
||||
router.register('audit/events', EventViewSet)
|
||||
router.register('sources/all', SourceViewSet)
|
||||
router.register('sources/ldap', LDAPSourceViewSet)
|
||||
router.register('sources/oauth', OAuthSourceViewSet)
|
||||
router.register('policies/all', PolicyViewSet)
|
||||
router.register('policies/passwordexpiry', PasswordExpiryPolicyViewSet)
|
||||
router.register('policies/groupmembership', GroupMembershipPolicyViewSet)
|
||||
router.register('policies/haveibeenpwned', HaveIBeenPwendPolicyViewSet)
|
||||
router.register('policies/fieldmatcher', FieldMatcherPolicyViewSet)
|
||||
router.register('policies/password', PasswordPolicyViewSet)
|
||||
router.register('policies/reputation', ReputationPolicyViewSet)
|
||||
router.register('policies/ssologin', SSOLoginPolicyViewSet)
|
||||
router.register('policies/webhook', WebhookPolicyViewSet)
|
||||
router.register('providers/all', ProviderViewSet)
|
||||
router.register('providers/applicationgateway', ApplicationGatewayProviderViewSet)
|
||||
router.register('providers/oauth', OAuth2ProviderViewSet)
|
||||
router.register('providers/openid', OpenIDProviderViewSet)
|
||||
router.register('providers/saml', SAMLProviderViewSet)
|
||||
router.register('propertymappings/all', PropertyMappingViewSet)
|
||||
router.register('propertymappings/ldap', LDAPPropertyMappingViewSet)
|
||||
router.register('propertymappings/saml', SAMLPropertyMappingViewSet)
|
||||
router.register('factors/all', FactorViewSet)
|
||||
router.register('factors/captcha', CaptchaFactorViewSet)
|
||||
router.register('factors/dummy', DummyFactorViewSet)
|
||||
router.register('factors/email', EmailFactorViewSet)
|
||||
router.register('factors/otp', OTPFactorViewSet)
|
||||
router.register('factors/password', PasswordFactorViewSet)
|
||||
router.register("core/applications", ApplicationViewSet)
|
||||
router.register("core/invitations", InvitationViewSet)
|
||||
router.register("core/groups", GroupViewSet)
|
||||
router.register("core/users", UserViewSet)
|
||||
router.register("audit/events", EventViewSet)
|
||||
router.register("sources/all", SourceViewSet)
|
||||
router.register("sources/ldap", LDAPSourceViewSet)
|
||||
router.register("sources/oauth", OAuthSourceViewSet)
|
||||
router.register("policies/all", PolicyViewSet)
|
||||
router.register("policies/passwordexpiry", PasswordExpiryPolicyViewSet)
|
||||
router.register("policies/groupmembership", GroupMembershipPolicyViewSet)
|
||||
router.register("policies/haveibeenpwned", HaveIBeenPwendPolicyViewSet)
|
||||
router.register("policies/fieldmatcher", FieldMatcherPolicyViewSet)
|
||||
router.register("policies/password", PasswordPolicyViewSet)
|
||||
router.register("policies/reputation", ReputationPolicyViewSet)
|
||||
router.register("policies/ssologin", SSOLoginPolicyViewSet)
|
||||
router.register("policies/webhook", WebhookPolicyViewSet)
|
||||
router.register("providers/all", ProviderViewSet)
|
||||
router.register("providers/applicationgateway", ApplicationGatewayProviderViewSet)
|
||||
router.register("providers/oauth", OAuth2ProviderViewSet)
|
||||
router.register("providers/openid", OpenIDProviderViewSet)
|
||||
router.register("providers/saml", SAMLProviderViewSet)
|
||||
router.register("propertymappings/all", PropertyMappingViewSet)
|
||||
router.register("propertymappings/ldap", LDAPPropertyMappingViewSet)
|
||||
router.register("propertymappings/saml", SAMLPropertyMappingViewSet)
|
||||
router.register("factors/all", FactorViewSet)
|
||||
router.register("factors/captcha", CaptchaFactorViewSet)
|
||||
router.register("factors/dummy", DummyFactorViewSet)
|
||||
router.register("factors/email", EmailFactorViewSet)
|
||||
router.register("factors/otp", OTPFactorViewSet)
|
||||
router.register("factors/password", PasswordFactorViewSet)
|
||||
|
||||
info = openapi.Info(
|
||||
title="passbook API",
|
||||
default_version='v2',
|
||||
default_version="v2",
|
||||
# description="Test description",
|
||||
# terms_of_service="https://www.google.com/policies/terms/",
|
||||
contact=openapi.Contact(email="hello@beryju.org"),
|
||||
license=openapi.License(name="MIT License"),
|
||||
)
|
||||
SchemaView = get_schema_view(
|
||||
info,
|
||||
public=True,
|
||||
permission_classes=(CustomObjectPermissions,),
|
||||
info, public=True, permission_classes=(CustomObjectPermissions,),
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^swagger(?P<format>\.json|\.yaml)$',
|
||||
SchemaView.without_ui(cache_timeout=0), name='schema-json'),
|
||||
path('swagger/', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||
path('redoc/', SchemaView.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
|
||||
url(
|
||||
r"^swagger(?P<format>\.json|\.yaml)$",
|
||||
SchemaView.without_ui(cache_timeout=0),
|
||||
name="schema-json",
|
||||
),
|
||||
path(
|
||||
"swagger/",
|
||||
SchemaView.with_ui("swagger", cache_timeout=0),
|
||||
name="schema-swagger-ui",
|
||||
),
|
||||
path("redoc/", SchemaView.with_ui("redoc", cache_timeout=0), name="schema-redoc"),
|
||||
] + router.urls
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
from passbook.lib.admin import admin_autoregister
|
||||
|
||||
admin_autoregister('passbook_audit')
|
||||
admin_autoregister("passbook_audit")
|
||||
|
|
|
@ -11,7 +11,16 @@ class EventSerializer(ModelSerializer):
|
|||
class Meta:
|
||||
|
||||
model = Event
|
||||
fields = ['pk', 'user', 'action', 'date', 'app', 'context', 'request_ip', 'created', ]
|
||||
fields = [
|
||||
"pk",
|
||||
"user",
|
||||
"action",
|
||||
"date",
|
||||
"app",
|
||||
"context",
|
||||
"request_ip",
|
||||
"created",
|
||||
]
|
||||
|
||||
|
||||
class EventViewSet(ReadOnlyModelViewSet):
|
||||
|
|
|
@ -7,10 +7,10 @@ from django.apps import AppConfig
|
|||
class PassbookAuditConfig(AppConfig):
|
||||
"""passbook audit app"""
|
||||
|
||||
name = 'passbook.audit'
|
||||
label = 'passbook_audit'
|
||||
verbose_name = 'passbook Audit'
|
||||
mountpoint = 'audit/'
|
||||
name = "passbook.audit"
|
||||
label = "passbook_audit"
|
||||
verbose_name = "passbook Audit"
|
||||
mountpoint = "audit/"
|
||||
|
||||
def ready(self):
|
||||
import_module('passbook.audit.signals')
|
||||
import_module("passbook.audit.signals")
|
||||
|
|
|
@ -18,20 +18,55 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AuditEntry',
|
||||
name="AuditEntry",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('action', models.TextField(choices=[('login', 'login'), ('login_failed', 'login_failed'), ('logout', 'logout'), ('authorize_application', 'authorize_application'), ('suspicious_request', 'suspicious_request'), ('sign_up', 'sign_up'), ('password_reset', 'password_reset'), ('invitation_created', 'invitation_created'), ('invitation_used', 'invitation_used')])),
|
||||
('date', models.DateTimeField(auto_now_add=True)),
|
||||
('app', models.TextField()),
|
||||
('context', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict)),
|
||||
('request_ip', models.GenericIPAddressField()),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"action",
|
||||
models.TextField(
|
||||
choices=[
|
||||
("login", "login"),
|
||||
("login_failed", "login_failed"),
|
||||
("logout", "logout"),
|
||||
("authorize_application", "authorize_application"),
|
||||
("suspicious_request", "suspicious_request"),
|
||||
("sign_up", "sign_up"),
|
||||
("password_reset", "password_reset"),
|
||||
("invitation_created", "invitation_created"),
|
||||
("invitation_used", "invitation_used"),
|
||||
]
|
||||
),
|
||||
),
|
||||
("date", models.DateTimeField(auto_now_add=True)),
|
||||
("app", models.TextField()),
|
||||
(
|
||||
"context",
|
||||
django.contrib.postgres.fields.jsonb.JSONField(
|
||||
blank=True, default=dict
|
||||
),
|
||||
),
|
||||
("request_ip", models.GenericIPAddressField()),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Audit Entry',
|
||||
'verbose_name_plural': 'Audit Entries',
|
||||
"verbose_name": "Audit Entry",
|
||||
"verbose_name_plural": "Audit Entries",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -8,12 +8,9 @@ class Migration(migrations.Migration):
|
|||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('passbook_audit', '0001_initial'),
|
||||
("passbook_audit", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='AuditEntry',
|
||||
new_name='Event',
|
||||
),
|
||||
migrations.RenameModel(old_name="AuditEntry", new_name="Event",),
|
||||
]
|
||||
|
|
|
@ -8,17 +8,33 @@ import passbook.audit.models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_audit', '0002_auto_20191028_0829'),
|
||||
("passbook_audit", "0002_auto_20191028_0829"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='event',
|
||||
options={'verbose_name': 'Audit Event', 'verbose_name_plural': 'Audit Events'},
|
||||
name="event",
|
||||
options={
|
||||
"verbose_name": "Audit Event",
|
||||
"verbose_name_plural": "Audit Events",
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='action',
|
||||
field=models.TextField(choices=[('LOGIN', 'login'), ('LOGIN_FAILED', 'login_failed'), ('LOGOUT', 'logout'), ('AUTHORIZE_APPLICATION', 'authorize_application'), ('SUSPICIOUS_REQUEST', 'suspicious_request'), ('SIGN_UP', 'sign_up'), ('PASSWORD_RESET', 'password_reset'), ('INVITE_CREATED', 'invitation_created'), ('INVITE_USED', 'invitation_used'), ('CUSTOM', 'custom')]),
|
||||
model_name="event",
|
||||
name="action",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("LOGIN", "login"),
|
||||
("LOGIN_FAILED", "login_failed"),
|
||||
("LOGOUT", "logout"),
|
||||
("AUTHORIZE_APPLICATION", "authorize_application"),
|
||||
("SUSPICIOUS_REQUEST", "suspicious_request"),
|
||||
("SIGN_UP", "sign_up"),
|
||||
("PASSWORD_RESET", "password_reset"),
|
||||
("INVITE_CREATED", "invitation_created"),
|
||||
("INVITE_USED", "invitation_used"),
|
||||
("CUSTOM", "custom"),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,17 +6,14 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_audit', '0003_auto_20191205_1407'),
|
||||
("passbook_audit", "0003_auto_20191205_1407"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='event',
|
||||
name='request_ip',
|
||||
),
|
||||
migrations.RemoveField(model_name="event", name="request_ip",),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='client_ip',
|
||||
model_name="event",
|
||||
name="client_ip",
|
||||
field=models.GenericIPAddressField(null=True),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -18,19 +18,20 @@ from passbook.lib.utils.http import get_client_ip
|
|||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class EventAction(Enum):
|
||||
"""All possible actions to save into the audit log"""
|
||||
|
||||
LOGIN = 'login'
|
||||
LOGIN_FAILED = 'login_failed'
|
||||
LOGOUT = 'logout'
|
||||
AUTHORIZE_APPLICATION = 'authorize_application'
|
||||
SUSPICIOUS_REQUEST = 'suspicious_request'
|
||||
SIGN_UP = 'sign_up'
|
||||
PASSWORD_RESET = 'password_reset' # noqa # nosec
|
||||
INVITE_CREATED = 'invitation_created'
|
||||
INVITE_USED = 'invitation_used'
|
||||
CUSTOM = 'custom'
|
||||
LOGIN = "login"
|
||||
LOGIN_FAILED = "login_failed"
|
||||
LOGOUT = "logout"
|
||||
AUTHORIZE_APPLICATION = "authorize_application"
|
||||
SUSPICIOUS_REQUEST = "suspicious_request"
|
||||
SIGN_UP = "sign_up"
|
||||
PASSWORD_RESET = "password_reset" # noqa # nosec
|
||||
INVITE_CREATED = "invitation_created"
|
||||
INVITE_USED = "invitation_used"
|
||||
CUSTOM = "custom"
|
||||
|
||||
@staticmethod
|
||||
def as_choices():
|
||||
|
@ -41,7 +42,9 @@ class EventAction(Enum):
|
|||
class Event(UUIDModel):
|
||||
"""An individual audit log event"""
|
||||
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL
|
||||
)
|
||||
action = models.TextField(choices=EventAction.as_choices())
|
||||
date = models.DateTimeField(auto_now_add=True)
|
||||
app = models.TextField()
|
||||
|
@ -56,28 +59,30 @@ class Event(UUIDModel):
|
|||
return request.resolver_match.app_name
|
||||
|
||||
@staticmethod
|
||||
def new(action: EventAction,
|
||||
app: Optional[str] = None,
|
||||
_inspect_offset: int = 1,
|
||||
**kwargs) -> 'Event':
|
||||
def new(
|
||||
action: EventAction,
|
||||
app: Optional[str] = None,
|
||||
_inspect_offset: int = 1,
|
||||
**kwargs,
|
||||
) -> "Event":
|
||||
"""Create new Event instance from arguments. Instance is NOT saved."""
|
||||
if not isinstance(action, EventAction):
|
||||
raise ValueError(f"action must be EventAction instance but was {type(action)}")
|
||||
raise ValueError(
|
||||
f"action must be EventAction instance but was {type(action)}"
|
||||
)
|
||||
if not app:
|
||||
app = getmodule(stack()[_inspect_offset][0]).__name__
|
||||
event = Event(
|
||||
action=action.value,
|
||||
app=app,
|
||||
context=kwargs)
|
||||
event = Event(action=action.value, app=app, context=kwargs)
|
||||
LOGGER.debug("Created Audit event", action=action, context=kwargs)
|
||||
return event
|
||||
|
||||
def from_http(self, request: HttpRequest,
|
||||
user: Optional[settings.AUTH_USER_MODEL] = None) -> 'Event':
|
||||
def from_http(
|
||||
self, request: HttpRequest, user: Optional[settings.AUTH_USER_MODEL] = None
|
||||
) -> "Event":
|
||||
"""Add data from a Django-HttpRequest, allowing the creation of
|
||||
Events independently from requests.
|
||||
`user` arguments optionally overrides user from requests."""
|
||||
if hasattr(request, 'user'):
|
||||
if hasattr(request, "user"):
|
||||
if isinstance(request.user, AnonymousUser):
|
||||
self.user = get_anonymous_user()
|
||||
else:
|
||||
|
@ -85,7 +90,7 @@ class Event(UUIDModel):
|
|||
if user:
|
||||
self.user = user
|
||||
# User 255.255.255.255 as fallback if IP cannot be determined
|
||||
self.client_ip = get_client_ip(request) or '255.255.255.255'
|
||||
self.client_ip = get_client_ip(request) or "255.255.255.255"
|
||||
# If there's no app set, we get it from the requests too
|
||||
if not self.app:
|
||||
self.app = Event._get_app_from_request(request)
|
||||
|
@ -94,10 +99,12 @@ class Event(UUIDModel):
|
|||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self._state.adding:
|
||||
raise ValidationError("you may not edit an existing %s" % self._meta.model_name)
|
||||
raise ValidationError(
|
||||
"you may not edit an existing %s" % self._meta.model_name
|
||||
)
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('Audit Event')
|
||||
verbose_name_plural = _('Audit Events')
|
||||
verbose_name = _("Audit Event")
|
||||
verbose_name_plural = _("Audit Events")
|
||||
|
|
|
@ -3,8 +3,7 @@ from django.contrib.auth.signals import user_logged_in, user_logged_out
|
|||
from django.dispatch import receiver
|
||||
|
||||
from passbook.audit.models import Event, EventAction
|
||||
from passbook.core.signals import (invitation_created, invitation_used,
|
||||
user_signed_up)
|
||||
from passbook.core.signals import invitation_created, invitation_used, user_signed_up
|
||||
|
||||
|
||||
@receiver(user_logged_in)
|
||||
|
@ -32,11 +31,15 @@ def on_user_signed_up(sender, request, user, **_):
|
|||
# pylint: disable=unused-argument
|
||||
def on_invitation_created(sender, request, invitation, **_):
|
||||
"""Log Invitation creation"""
|
||||
Event.new(EventAction.INVITE_CREATED, invitation_uuid=invitation.uuid.hex).from_http(request)
|
||||
Event.new(
|
||||
EventAction.INVITE_CREATED, invitation_uuid=invitation.uuid.hex
|
||||
).from_http(request)
|
||||
|
||||
|
||||
@receiver(invitation_used)
|
||||
# pylint: disable=unused-argument
|
||||
def on_invitation_used(sender, request, invitation, **_):
|
||||
"""Log Invitation usage"""
|
||||
Event.new(EventAction.INVITE_USED, invitation_uuid=invitation.uuid.hex).from_http(request)
|
||||
Event.new(EventAction.INVITE_USED, invitation_uuid=invitation.uuid.hex).from_http(
|
||||
request
|
||||
)
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
from passbook.lib.admin import admin_autoregister
|
||||
|
||||
admin_autoregister('passbook_core')
|
||||
admin_autoregister("passbook_core")
|
||||
|
|
|
@ -11,8 +11,16 @@ class ApplicationSerializer(ModelSerializer):
|
|||
class Meta:
|
||||
|
||||
model = Application
|
||||
fields = ['pk', 'name', 'slug', 'launch_url', 'icon_url',
|
||||
'provider', 'policies', 'skip_authorization']
|
||||
fields = [
|
||||
"pk",
|
||||
"name",
|
||||
"slug",
|
||||
"launch_url",
|
||||
"icon_url",
|
||||
"provider",
|
||||
"policies",
|
||||
"skip_authorization",
|
||||
]
|
||||
|
||||
|
||||
class ApplicationViewSet(ModelViewSet):
|
||||
|
|
|
@ -8,16 +8,16 @@ from passbook.core.models import Factor
|
|||
class FactorSerializer(ModelSerializer):
|
||||
"""Factor Serializer"""
|
||||
|
||||
__type__ = SerializerMethodField(method_name='get_type')
|
||||
__type__ = SerializerMethodField(method_name="get_type")
|
||||
|
||||
def get_type(self, obj):
|
||||
"""Get object type so that we know which API Endpoint to use to get the full object"""
|
||||
return obj._meta.object_name.lower().replace('factor', '')
|
||||
return obj._meta.object_name.lower().replace("factor", "")
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Factor
|
||||
fields = ['pk', 'name', 'slug', 'order', 'enabled', '__type__']
|
||||
fields = ["pk", "name", "slug", "order", "enabled", "__type__"]
|
||||
|
||||
|
||||
class FactorViewSet(ReadOnlyModelViewSet):
|
||||
|
|
|
@ -11,7 +11,7 @@ class GroupSerializer(ModelSerializer):
|
|||
class Meta:
|
||||
|
||||
model = Group
|
||||
fields = ['pk', 'name', 'parent', 'user_set', 'attributes']
|
||||
fields = ["pk", "name", "parent", "user_set", "attributes"]
|
||||
|
||||
|
||||
class GroupViewSet(ModelViewSet):
|
||||
|
|
|
@ -11,7 +11,13 @@ class InvitationSerializer(ModelSerializer):
|
|||
class Meta:
|
||||
|
||||
model = Invitation
|
||||
fields = ['pk', 'expires', 'fixed_username', 'fixed_email', 'needs_confirmation']
|
||||
fields = [
|
||||
"pk",
|
||||
"expires",
|
||||
"fixed_username",
|
||||
"fixed_email",
|
||||
"needs_confirmation",
|
||||
]
|
||||
|
||||
|
||||
class InvitationViewSet(ModelViewSet):
|
||||
|
|
|
@ -9,16 +9,16 @@ from passbook.policies.forms import GENERAL_FIELDS
|
|||
class PolicySerializer(ModelSerializer):
|
||||
"""Policy Serializer"""
|
||||
|
||||
__type__ = SerializerMethodField(method_name='get_type')
|
||||
__type__ = SerializerMethodField(method_name="get_type")
|
||||
|
||||
def get_type(self, obj):
|
||||
"""Get object type so that we know which API Endpoint to use to get the full object"""
|
||||
return obj._meta.object_name.lower().replace('policy', '')
|
||||
return obj._meta.object_name.lower().replace("policy", "")
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Policy
|
||||
fields = ['pk'] + GENERAL_FIELDS + ['__type__']
|
||||
fields = ["pk"] + GENERAL_FIELDS + ["__type__"]
|
||||
|
||||
|
||||
class PolicyViewSet(ReadOnlyModelViewSet):
|
||||
|
|
|
@ -8,16 +8,16 @@ from passbook.core.models import PropertyMapping
|
|||
class PropertyMappingSerializer(ModelSerializer):
|
||||
"""PropertyMapping Serializer"""
|
||||
|
||||
__type__ = SerializerMethodField(method_name='get_type')
|
||||
__type__ = SerializerMethodField(method_name="get_type")
|
||||
|
||||
def get_type(self, obj):
|
||||
"""Get object type so that we know which API Endpoint to use to get the full object"""
|
||||
return obj._meta.object_name.lower().replace('propertymapping', '')
|
||||
return obj._meta.object_name.lower().replace("propertymapping", "")
|
||||
|
||||
class Meta:
|
||||
|
||||
model = PropertyMapping
|
||||
fields = ['pk', 'name', '__type__']
|
||||
fields = ["pk", "name", "__type__"]
|
||||
|
||||
|
||||
class PropertyMappingViewSet(ReadOnlyModelViewSet):
|
||||
|
|
|
@ -8,16 +8,16 @@ from passbook.core.models import Provider
|
|||
class ProviderSerializer(ModelSerializer):
|
||||
"""Provider Serializer"""
|
||||
|
||||
__type__ = SerializerMethodField(method_name='get_type')
|
||||
__type__ = SerializerMethodField(method_name="get_type")
|
||||
|
||||
def get_type(self, obj):
|
||||
"""Get object type so that we know which API Endpoint to use to get the full object"""
|
||||
return obj._meta.object_name.lower().replace('provider', '')
|
||||
return obj._meta.object_name.lower().replace("provider", "")
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Provider
|
||||
fields = ['pk', 'property_mappings', '__type__']
|
||||
fields = ["pk", "property_mappings", "__type__"]
|
||||
|
||||
|
||||
class ProviderViewSet(ReadOnlyModelViewSet):
|
||||
|
|
|
@ -9,16 +9,16 @@ from passbook.core.models import Source
|
|||
class SourceSerializer(ModelSerializer):
|
||||
"""Source Serializer"""
|
||||
|
||||
__type__ = SerializerMethodField(method_name='get_type')
|
||||
__type__ = SerializerMethodField(method_name="get_type")
|
||||
|
||||
def get_type(self, obj):
|
||||
"""Get object type so that we know which API Endpoint to use to get the full object"""
|
||||
return obj._meta.object_name.lower().replace('source', '')
|
||||
return obj._meta.object_name.lower().replace("source", "")
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Source
|
||||
fields = SOURCE_SERIALIZER_FIELDS + ['__type__']
|
||||
fields = SOURCE_SERIALIZER_FIELDS + ["__type__"]
|
||||
|
||||
|
||||
class SourceViewSet(ReadOnlyModelViewSet):
|
||||
|
|
|
@ -11,7 +11,7 @@ class UserSerializer(ModelSerializer):
|
|||
class Meta:
|
||||
|
||||
model = User
|
||||
fields = ['pk', 'username', 'name', 'email']
|
||||
fields = ["pk", "username", "name", "email"]
|
||||
|
||||
|
||||
class UserViewSet(ModelViewSet):
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.apps import AppConfig
|
|||
class PassbookCoreConfig(AppConfig):
|
||||
"""passbook core app config"""
|
||||
|
||||
name = 'passbook.core'
|
||||
label = 'passbook_core'
|
||||
verbose_name = 'passbook Core'
|
||||
mountpoint = ''
|
||||
name = "passbook.core"
|
||||
label = "passbook_core"
|
||||
verbose_name = "passbook Core"
|
||||
mountpoint = ""
|
||||
|
|
|
@ -9,21 +9,29 @@ from passbook.core.models import Application, Provider
|
|||
class ApplicationForm(forms.ModelForm):
|
||||
"""Application Form"""
|
||||
|
||||
provider = forms.ModelChoiceField(queryset=Provider.objects.all().select_subclasses(),
|
||||
required=False)
|
||||
provider = forms.ModelChoiceField(
|
||||
queryset=Provider.objects.all().select_subclasses(), required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Application
|
||||
fields = ['name', 'slug', 'launch_url', 'icon_url',
|
||||
'provider', 'policies', 'skip_authorization']
|
||||
fields = [
|
||||
"name",
|
||||
"slug",
|
||||
"launch_url",
|
||||
"icon_url",
|
||||
"provider",
|
||||
"policies",
|
||||
"skip_authorization",
|
||||
]
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
'launch_url': forms.TextInput(),
|
||||
'icon_url': forms.TextInput(),
|
||||
'policies': FilteredSelectMultiple(_('policies'), False)
|
||||
"name": forms.TextInput(),
|
||||
"launch_url": forms.TextInput(),
|
||||
"icon_url": forms.TextInput(),
|
||||
"policies": FilteredSelectMultiple(_("policies"), False),
|
||||
}
|
||||
labels = {
|
||||
'launch_url': _('Launch URL'),
|
||||
'icon_url': _('Icon URL'),
|
||||
"launch_url": _("Launch URL"),
|
||||
"icon_url": _("Icon URL"),
|
||||
}
|
||||
|
|
|
@ -11,55 +11,64 @@ from passbook.lib.utils.ui import human_list
|
|||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
"""Allow users to login"""
|
||||
|
||||
title = _('Log in to your account')
|
||||
title = _("Log in to your account")
|
||||
uid_field = forms.CharField()
|
||||
remember_me = forms.BooleanField(required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if CONFIG.y('passbook.uid_fields') == ['e-mail']:
|
||||
self.fields['uid_field'] = forms.EmailField()
|
||||
self.fields['uid_field'].widget.attrs = {
|
||||
'placeholder': _(human_list([x.title() for x in CONFIG.y('passbook.uid_fields')]))
|
||||
if CONFIG.y("passbook.uid_fields") == ["e-mail"]:
|
||||
self.fields["uid_field"] = forms.EmailField()
|
||||
self.fields["uid_field"].widget.attrs = {
|
||||
"placeholder": _(
|
||||
human_list([x.title() for x in CONFIG.y("passbook.uid_fields")])
|
||||
)
|
||||
}
|
||||
|
||||
def clean_uid_field(self):
|
||||
"""Validate uid_field after EmailValidator if 'email' is the only selected uid_fields"""
|
||||
if CONFIG.y('passbook.uid_fields') == ['email']:
|
||||
validate_email(self.cleaned_data.get('uid_field'))
|
||||
return self.cleaned_data.get('uid_field')
|
||||
if CONFIG.y("passbook.uid_fields") == ["email"]:
|
||||
validate_email(self.cleaned_data.get("uid_field"))
|
||||
return self.cleaned_data.get("uid_field")
|
||||
|
||||
|
||||
class SignUpForm(forms.Form):
|
||||
"""SignUp Form"""
|
||||
|
||||
title = _('Sign Up')
|
||||
name = forms.CharField(label=_('Name'),
|
||||
widget=forms.TextInput(attrs={'placeholder': _('Name')}))
|
||||
username = forms.CharField(label=_('Username'),
|
||||
widget=forms.TextInput(attrs={'placeholder': _('Username')}))
|
||||
email = forms.EmailField(label=_('E-Mail'),
|
||||
widget=forms.TextInput(attrs={'placeholder': _('E-Mail')}))
|
||||
password = forms.CharField(label=_('Password'),
|
||||
widget=forms.PasswordInput(attrs={'placeholder': _('Password')}))
|
||||
password_repeat = forms.CharField(label=_('Repeat Password'),
|
||||
widget=forms.PasswordInput(attrs={
|
||||
'placeholder': _('Repeat Password')
|
||||
}))
|
||||
title = _("Sign Up")
|
||||
name = forms.CharField(
|
||||
label=_("Name"), widget=forms.TextInput(attrs={"placeholder": _("Name")})
|
||||
)
|
||||
username = forms.CharField(
|
||||
label=_("Username"),
|
||||
widget=forms.TextInput(attrs={"placeholder": _("Username")}),
|
||||
)
|
||||
email = forms.EmailField(
|
||||
label=_("E-Mail"), widget=forms.TextInput(attrs={"placeholder": _("E-Mail")})
|
||||
)
|
||||
password = forms.CharField(
|
||||
label=_("Password"),
|
||||
widget=forms.PasswordInput(attrs={"placeholder": _("Password")}),
|
||||
)
|
||||
password_repeat = forms.CharField(
|
||||
label=_("Repeat Password"),
|
||||
widget=forms.PasswordInput(attrs={"placeholder": _("Repeat Password")}),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# All fields which have initial data supplied are set to read only
|
||||
if 'initial' in kwargs:
|
||||
for field in kwargs.get('initial').keys():
|
||||
self.fields[field].widget.attrs['readonly'] = 'readonly'
|
||||
if "initial" in kwargs:
|
||||
for field in kwargs.get("initial").keys():
|
||||
self.fields[field].widget.attrs["readonly"] = "readonly"
|
||||
|
||||
def clean_username(self):
|
||||
"""Check if username is used already"""
|
||||
username = self.cleaned_data.get('username')
|
||||
username = self.cleaned_data.get("username")
|
||||
if User.objects.filter(username=username).exists():
|
||||
LOGGER.warning("Username %s already exists", username)
|
||||
raise ValidationError(_("Username already exists"))
|
||||
|
@ -67,7 +76,7 @@ class SignUpForm(forms.Form):
|
|||
|
||||
def clean_email(self):
|
||||
"""Check if email is already used in django or other auth sources"""
|
||||
email = self.cleaned_data.get('email')
|
||||
email = self.cleaned_data.get("email")
|
||||
# Check if user exists already, error early
|
||||
if User.objects.filter(email=email).exists():
|
||||
LOGGER.debug("email %s exists in django", email)
|
||||
|
@ -76,8 +85,8 @@ class SignUpForm(forms.Form):
|
|||
|
||||
def clean_password_repeat(self):
|
||||
"""Check if Password adheres to filter and if passwords matche"""
|
||||
password = self.cleaned_data.get('password')
|
||||
password_repeat = self.cleaned_data.get('password_repeat')
|
||||
password = self.cleaned_data.get("password")
|
||||
password_repeat = self.cleaned_data.get("password_repeat")
|
||||
if password != password_repeat:
|
||||
raise ValidationError(_("Passwords don't match"))
|
||||
return self.cleaned_data.get('password_repeat')
|
||||
return self.cleaned_data.get("password_repeat")
|
||||
|
|
|
@ -9,24 +9,29 @@ class GroupForm(forms.ModelForm):
|
|||
"""Group Form"""
|
||||
|
||||
members = forms.ModelMultipleChoiceField(
|
||||
User.objects.all(), required=False, widget=FilteredSelectMultiple('users', False))
|
||||
User.objects.all(),
|
||||
required=False,
|
||||
widget=FilteredSelectMultiple("users", False),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.instance.pk:
|
||||
self.initial['members'] = self.instance.user_set.values_list('pk', flat=True)
|
||||
self.initial["members"] = self.instance.user_set.values_list(
|
||||
"pk", flat=True
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
instance = super().save(*args, **kwargs)
|
||||
if instance.pk:
|
||||
instance.user_set.clear()
|
||||
instance.user_set.add(*self.cleaned_data['members'])
|
||||
instance.user_set.add(*self.cleaned_data["members"])
|
||||
return instance
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Group
|
||||
fields = ['name', 'parent', 'members', 'attributes']
|
||||
fields = ["name", "parent", "members", "attributes"]
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
"name": forms.TextInput(),
|
||||
}
|
||||
|
|
|
@ -12,27 +12,27 @@ class InvitationForm(forms.ModelForm):
|
|||
|
||||
def clean_fixed_username(self):
|
||||
"""Check if username is already used"""
|
||||
username = self.cleaned_data.get('fixed_username')
|
||||
username = self.cleaned_data.get("fixed_username")
|
||||
if User.objects.filter(username=username).exists():
|
||||
raise ValidationError(_('Username is already in use.'))
|
||||
raise ValidationError(_("Username is already in use."))
|
||||
return username
|
||||
|
||||
def clean_fixed_email(self):
|
||||
"""Check if email is already used"""
|
||||
email = self.cleaned_data.get('fixed_email')
|
||||
email = self.cleaned_data.get("fixed_email")
|
||||
if User.objects.filter(email=email).exists():
|
||||
raise ValidationError(_('E-Mail is already in use.'))
|
||||
raise ValidationError(_("E-Mail is already in use."))
|
||||
return email
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Invitation
|
||||
fields = ['expires', 'fixed_username', 'fixed_email', 'needs_confirmation']
|
||||
fields = ["expires", "fixed_username", "fixed_email", "needs_confirmation"]
|
||||
labels = {
|
||||
'fixed_username': "Force user's username (optional)",
|
||||
'fixed_email': "Force user's email (optional)",
|
||||
"fixed_username": "Force user's username (optional)",
|
||||
"fixed_email": "Force user's email (optional)",
|
||||
}
|
||||
widgets = {
|
||||
'fixed_username': forms.TextInput(),
|
||||
'fixed_email': forms.TextInput(),
|
||||
"fixed_username": forms.TextInput(),
|
||||
"fixed_email": forms.TextInput(),
|
||||
}
|
||||
|
|
|
@ -13,10 +13,8 @@ class DebugPolicyForm(forms.ModelForm):
|
|||
class Meta:
|
||||
|
||||
model = DebugPolicy
|
||||
fields = GENERAL_FIELDS + ['result', 'wait_min', 'wait_max']
|
||||
fields = GENERAL_FIELDS + ["result", "wait_min", "wait_max"]
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
}
|
||||
labels = {
|
||||
'result': _('Allow user')
|
||||
"name": forms.TextInput(),
|
||||
}
|
||||
labels = {"result": _("Allow user")}
|
||||
|
|
|
@ -13,29 +13,30 @@ class UserDetailForm(forms.ModelForm):
|
|||
class Meta:
|
||||
|
||||
model = User
|
||||
fields = ['username', 'name', 'email']
|
||||
widgets = {
|
||||
'name': forms.TextInput
|
||||
}
|
||||
fields = ["username", "name", "email"]
|
||||
widgets = {"name": forms.TextInput}
|
||||
|
||||
|
||||
class PasswordChangeForm(forms.Form):
|
||||
"""Form to update password"""
|
||||
|
||||
password = forms.CharField(label=_('Password'),
|
||||
widget=forms.PasswordInput(attrs={
|
||||
'placeholder': _('New Password'),
|
||||
'autocomplete': 'new-password'
|
||||
}))
|
||||
password_repeat = forms.CharField(label=_('Repeat Password'),
|
||||
widget=forms.PasswordInput(attrs={
|
||||
'placeholder': _('Repeat Password'),
|
||||
'autocomplete': 'new-password'
|
||||
}))
|
||||
password = forms.CharField(
|
||||
label=_("Password"),
|
||||
widget=forms.PasswordInput(
|
||||
attrs={"placeholder": _("New Password"), "autocomplete": "new-password"}
|
||||
),
|
||||
)
|
||||
password_repeat = forms.CharField(
|
||||
label=_("Repeat Password"),
|
||||
widget=forms.PasswordInput(
|
||||
attrs={"placeholder": _("Repeat Password"), "autocomplete": "new-password"}
|
||||
),
|
||||
)
|
||||
|
||||
def clean_password_repeat(self):
|
||||
"""Check if Password adheres to filter and if passwords matche"""
|
||||
password = self.cleaned_data.get('password')
|
||||
password_repeat = self.cleaned_data.get('password_repeat')
|
||||
password = self.cleaned_data.get("password")
|
||||
password_repeat = self.cleaned_data.get("password_repeat")
|
||||
if password != password_repeat:
|
||||
raise ValidationError(_("Passwords don't match"))
|
||||
return self.cleaned_data.get('password_repeat')
|
||||
return self.cleaned_data.get("password_repeat")
|
||||
|
|
|
@ -18,206 +18,433 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0011_update_proxy_permissions'),
|
||||
("auth", "0011_update_proxy_permissions"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
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(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
|
||||
('name', models.TextField()),
|
||||
('password_change_date', models.DateTimeField(auto_now_add=True)),
|
||||
(
|
||||
"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(
|
||||
error_messages={
|
||||
"unique": "A user with that username already exists."
|
||||
},
|
||||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=150,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.contrib.auth.validators.UnicodeUsernameValidator()
|
||||
],
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=30, verbose_name="first name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"last_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=150, verbose_name="last name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
blank=True, max_length=254, verbose_name="email address"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_staff",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates whether the user can log into this admin site.",
|
||||
verbose_name="staff status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_active",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
|
||||
verbose_name="active",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date_joined",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="date joined"
|
||||
),
|
||||
),
|
||||
("uuid", models.UUIDField(default=uuid.uuid4, editable=False)),
|
||||
("name", models.TextField()),
|
||||
("password_change_date", models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'user',
|
||||
'verbose_name_plural': 'users',
|
||||
'abstract': False,
|
||||
"verbose_name": "user",
|
||||
"verbose_name_plural": "users",
|
||||
"abstract": False,
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
managers=[("objects", django.contrib.auth.models.UserManager()),],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Policy',
|
||||
name="Policy",
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('name', models.TextField(blank=True, null=True)),
|
||||
('action', models.CharField(choices=[('allow', 'allow'), ('deny', 'deny')], max_length=20)),
|
||||
('negate', models.BooleanField(default=False)),
|
||||
('order', models.IntegerField(default=0)),
|
||||
('timeout', models.IntegerField(default=30)),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("last_updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("name", models.TextField(blank=True, null=True)),
|
||||
(
|
||||
"action",
|
||||
models.CharField(
|
||||
choices=[("allow", "allow"), ("deny", "deny")], max_length=20
|
||||
),
|
||||
),
|
||||
("negate", models.BooleanField(default=False)),
|
||||
("order", models.IntegerField(default=0)),
|
||||
("timeout", models.IntegerField(default=30)),
|
||||
],
|
||||
options={"abstract": False,},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="PolicyModel",
|
||||
fields=[
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("last_updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"policies",
|
||||
models.ManyToManyField(blank=True, to="passbook_core.Policy"),
|
||||
),
|
||||
],
|
||||
options={"abstract": False,},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="PropertyMapping",
|
||||
fields=[
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
"verbose_name": "Property Mapping",
|
||||
"verbose_name_plural": "Property Mappings",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PolicyModel',
|
||||
name="DebugPolicy",
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('policies', models.ManyToManyField(blank=True, to='passbook_core.Policy')),
|
||||
(
|
||||
"policy_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
("result", models.BooleanField(default=False)),
|
||||
("wait_min", models.IntegerField(default=5)),
|
||||
("wait_max", models.IntegerField(default=30)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
"verbose_name": "Debug Policy",
|
||||
"verbose_name_plural": "Debug Policies",
|
||||
},
|
||||
bases=("passbook_core.policy",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PropertyMapping',
|
||||
name="Factor",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('name', models.TextField()),
|
||||
(
|
||||
"policymodel_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_core.PolicyModel",
|
||||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
("slug", models.SlugField(unique=True)),
|
||||
("order", models.IntegerField()),
|
||||
("enabled", models.BooleanField(default=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Property Mapping',
|
||||
'verbose_name_plural': 'Property Mappings',
|
||||
},
|
||||
options={"abstract": False,},
|
||||
bases=("passbook_core.policymodel",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DebugPolicy',
|
||||
name="Source",
|
||||
fields=[
|
||||
('policy_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Policy')),
|
||||
('result', models.BooleanField(default=False)),
|
||||
('wait_min', models.IntegerField(default=5)),
|
||||
('wait_max', models.IntegerField(default=30)),
|
||||
(
|
||||
"policymodel_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_core.PolicyModel",
|
||||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
("slug", models.SlugField()),
|
||||
("enabled", models.BooleanField(default=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Debug Policy',
|
||||
'verbose_name_plural': 'Debug Policies',
|
||||
},
|
||||
bases=('passbook_core.policy',),
|
||||
options={"abstract": False,},
|
||||
bases=("passbook_core.policymodel",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Factor',
|
||||
name="Provider",
|
||||
fields=[
|
||||
('policymodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.PolicyModel')),
|
||||
('name', models.TextField()),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
('order', models.IntegerField()),
|
||||
('enabled', models.BooleanField(default=True)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('passbook_core.policymodel',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Source',
|
||||
fields=[
|
||||
('policymodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.PolicyModel')),
|
||||
('name', models.TextField()),
|
||||
('slug', models.SlugField()),
|
||||
('enabled', models.BooleanField(default=True)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('passbook_core.policymodel',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Provider',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('property_mappings', models.ManyToManyField(blank=True, default=None, to='passbook_core.PropertyMapping')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"property_mappings",
|
||||
models.ManyToManyField(
|
||||
blank=True, default=None, to="passbook_core.PropertyMapping"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Nonce',
|
||||
name="Nonce",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('expires', models.DateTimeField(default=passbook.core.models.default_nonce_duration)),
|
||||
('expiring', models.BooleanField(default=True)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"expires",
|
||||
models.DateTimeField(
|
||||
default=passbook.core.models.default_nonce_duration
|
||||
),
|
||||
),
|
||||
("expiring", models.BooleanField(default=True)),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={"verbose_name": "Nonce", "verbose_name_plural": "Nonces",},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Invitation",
|
||||
fields=[
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("expires", models.DateTimeField(blank=True, default=None, null=True)),
|
||||
("fixed_username", models.TextField(blank=True, default=None)),
|
||||
("fixed_email", models.TextField(blank=True, default=None)),
|
||||
("needs_confirmation", models.BooleanField(default=True)),
|
||||
(
|
||||
"created_by",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Nonce',
|
||||
'verbose_name_plural': 'Nonces',
|
||||
"verbose_name": "Invitation",
|
||||
"verbose_name_plural": "Invitations",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Invitation',
|
||||
name="Group",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('expires', models.DateTimeField(blank=True, default=None, null=True)),
|
||||
('fixed_username', models.TextField(blank=True, default=None)),
|
||||
('fixed_email', models.TextField(blank=True, default=None)),
|
||||
('needs_confirmation', models.BooleanField(default=True)),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=80, verbose_name="name")),
|
||||
(
|
||||
"tags",
|
||||
django.contrib.postgres.fields.jsonb.JSONField(
|
||||
blank=True, default=dict
|
||||
),
|
||||
),
|
||||
(
|
||||
"parent",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="children",
|
||||
to="passbook_core.Group",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Invitation',
|
||||
'verbose_name_plural': 'Invitations',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Group',
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=80, verbose_name='name')),
|
||||
('tags', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict)),
|
||||
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='passbook_core.Group')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('name', 'parent')},
|
||||
},
|
||||
options={"unique_together": {("name", "parent")},},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='groups',
|
||||
field=models.ManyToManyField(to='passbook_core.Group'),
|
||||
model_name="user",
|
||||
name="groups",
|
||||
field=models.ManyToManyField(to="passbook_core.Group"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='user_permissions',
|
||||
field=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'),
|
||||
model_name="user",
|
||||
name="user_permissions",
|
||||
field=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",
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserSourceConnection',
|
||||
name="UserSourceConnection",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='passbook_core.Source')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("last_updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"source",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="passbook_core.Source",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('user', 'source')},
|
||||
},
|
||||
options={"unique_together": {("user", "source")},},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Application',
|
||||
name="Application",
|
||||
fields=[
|
||||
('policymodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.PolicyModel')),
|
||||
('name', models.TextField()),
|
||||
('slug', models.SlugField()),
|
||||
('launch_url', models.URLField(blank=True, null=True)),
|
||||
('icon_url', models.TextField(blank=True, null=True)),
|
||||
('skip_authorization', models.BooleanField(default=False)),
|
||||
('provider', models.OneToOneField(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='passbook_core.Provider')),
|
||||
(
|
||||
"policymodel_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_core.PolicyModel",
|
||||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
("slug", models.SlugField()),
|
||||
("launch_url", models.URLField(blank=True, null=True)),
|
||||
("icon_url", models.TextField(blank=True, null=True)),
|
||||
("skip_authorization", models.BooleanField(default=False)),
|
||||
(
|
||||
"provider",
|
||||
models.OneToOneField(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_DEFAULT,
|
||||
to="passbook_core.Provider",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('passbook_core.policymodel',),
|
||||
options={"abstract": False,},
|
||||
bases=("passbook_core.policymodel",),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='sources',
|
||||
field=models.ManyToManyField(through='passbook_core.UserSourceConnection', to='passbook_core.Source'),
|
||||
model_name="user",
|
||||
name="sources",
|
||||
field=models.ManyToManyField(
|
||||
through="passbook_core.UserSourceConnection", to="passbook_core.Source"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,12 +6,12 @@ from django.db import migrations
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0001_initial'),
|
||||
("passbook_core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='user',
|
||||
options={'permissions': (('reset_user_password', 'Reset Password'),)},
|
||||
name="user",
|
||||
options={"permissions": (("reset_user_password", "Reset Password"),)},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,13 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0001_initial'),
|
||||
("passbook_core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='nonce',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default=''),
|
||||
model_name="nonce",
|
||||
name="description",
|
||||
field=models.TextField(blank=True, default=""),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,23 +7,25 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0002_nonce_description'),
|
||||
("passbook_core", "0002_nonce_description"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='group',
|
||||
old_name='tags',
|
||||
new_name='attributes',
|
||||
model_name="group", old_name="tags", new_name="attributes",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='source',
|
||||
name='property_mappings',
|
||||
field=models.ManyToManyField(blank=True, default=None, to='passbook_core.PropertyMapping'),
|
||||
model_name="source",
|
||||
name="property_mappings",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, default=None, to="passbook_core.PropertyMapping"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='attributes',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict),
|
||||
model_name="user",
|
||||
name="attributes",
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(
|
||||
blank=True, default=dict
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,9 +6,8 @@ from django.db import migrations
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0002_auto_20191010_1058'),
|
||||
('passbook_core', '0002_nonce_description'),
|
||||
("passbook_core", "0002_auto_20191010_1058"),
|
||||
("passbook_core", "0002_nonce_description"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
operations = []
|
||||
|
|
|
@ -6,12 +6,9 @@ from django.db import migrations
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0003_auto_20191011_0914'),
|
||||
("passbook_core", "0003_auto_20191011_0914"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='policy',
|
||||
name='action',
|
||||
),
|
||||
migrations.RemoveField(model_name="policy", name="action",),
|
||||
]
|
||||
|
|
|
@ -6,9 +6,8 @@ from django.db import migrations
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0004_remove_policy_action'),
|
||||
('passbook_core', '0003_merge_20191010_1541'),
|
||||
("passbook_core", "0004_remove_policy_action"),
|
||||
("passbook_core", "0003_merge_20191010_1541"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
operations = []
|
||||
|
|
|
@ -27,12 +27,18 @@ def default_nonce_duration():
|
|||
"""Default duration a Nonce is valid"""
|
||||
return now() + timedelta(hours=4)
|
||||
|
||||
|
||||
class Group(UUIDModel):
|
||||
"""Custom Group model which supports a basic hierarchy"""
|
||||
|
||||
name = models.CharField(_('name'), max_length=80)
|
||||
parent = models.ForeignKey('Group', blank=True, null=True,
|
||||
on_delete=models.SET_NULL, related_name='children')
|
||||
name = models.CharField(_("name"), max_length=80)
|
||||
parent = models.ForeignKey(
|
||||
"Group",
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="children",
|
||||
)
|
||||
attributes = JSONField(default=dict, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -40,7 +46,8 @@ class Group(UUIDModel):
|
|||
|
||||
class Meta:
|
||||
|
||||
unique_together = (('name', 'parent',),)
|
||||
unique_together = (("name", "parent",),)
|
||||
|
||||
|
||||
class User(GuardianUserMixin, AbstractUser):
|
||||
"""Custom User model to allow easier adding o f user-based settings"""
|
||||
|
@ -48,8 +55,8 @@ class User(GuardianUserMixin, AbstractUser):
|
|||
uuid = models.UUIDField(default=uuid4, editable=False)
|
||||
name = models.TextField()
|
||||
|
||||
sources = models.ManyToManyField('Source', through='UserSourceConnection')
|
||||
groups = models.ManyToManyField('Group')
|
||||
sources = models.ManyToManyField("Source", through="UserSourceConnection")
|
||||
groups = models.ManyToManyField("Group")
|
||||
password_change_date = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
attributes = JSONField(default=dict, blank=True)
|
||||
|
@ -62,28 +69,29 @@ class User(GuardianUserMixin, AbstractUser):
|
|||
|
||||
class Meta:
|
||||
|
||||
permissions = (
|
||||
('reset_user_password', 'Reset Password'),
|
||||
)
|
||||
permissions = (("reset_user_password", "Reset Password"),)
|
||||
|
||||
|
||||
class Provider(models.Model):
|
||||
"""Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
|
||||
|
||||
property_mappings = models.ManyToManyField('PropertyMapping', default=None, blank=True)
|
||||
property_mappings = models.ManyToManyField(
|
||||
"PropertyMapping", default=None, blank=True
|
||||
)
|
||||
|
||||
objects = InheritanceManager()
|
||||
|
||||
# This class defines no field for easier inheritance
|
||||
def __str__(self):
|
||||
if hasattr(self, 'name'):
|
||||
return getattr(self, 'name')
|
||||
if hasattr(self, "name"):
|
||||
return getattr(self, "name")
|
||||
return super().__str__()
|
||||
|
||||
|
||||
class PolicyModel(UUIDModel, CreatedUpdatedModel):
|
||||
"""Base model which can have policies applied to it"""
|
||||
|
||||
policies = models.ManyToManyField('Policy', blank=True)
|
||||
policies = models.ManyToManyField("Policy", blank=True)
|
||||
|
||||
|
||||
class UserSettings:
|
||||
|
@ -108,8 +116,8 @@ class Factor(PolicyModel):
|
|||
enabled = models.BooleanField(default=True)
|
||||
|
||||
objects = InheritanceManager()
|
||||
type = ''
|
||||
form = ''
|
||||
type = ""
|
||||
form = ""
|
||||
|
||||
def user_settings(self) -> Optional[UserSettings]:
|
||||
"""Entrypoint to integrate with User settings. Can either return None if no
|
||||
|
@ -129,8 +137,9 @@ class Application(PolicyModel):
|
|||
slug = models.SlugField()
|
||||
launch_url = models.URLField(null=True, blank=True)
|
||||
icon_url = models.TextField(null=True, blank=True)
|
||||
provider = models.OneToOneField('Provider', null=True, blank=True,
|
||||
default=None, on_delete=models.SET_DEFAULT)
|
||||
provider = models.OneToOneField(
|
||||
"Provider", null=True, blank=True, default=None, on_delete=models.SET_DEFAULT
|
||||
)
|
||||
skip_authorization = models.BooleanField(default=False)
|
||||
|
||||
objects = InheritanceManager()
|
||||
|
@ -151,9 +160,11 @@ class Source(PolicyModel):
|
|||
name = models.TextField()
|
||||
slug = models.SlugField()
|
||||
enabled = models.BooleanField(default=True)
|
||||
property_mappings = models.ManyToManyField('PropertyMapping', default=None, blank=True)
|
||||
property_mappings = models.ManyToManyField(
|
||||
"PropertyMapping", default=None, blank=True
|
||||
)
|
||||
|
||||
form = '' # ModelForm-based class ued to create/edit instance
|
||||
form = "" # ModelForm-based class ued to create/edit instance
|
||||
|
||||
objects = InheritanceManager()
|
||||
|
||||
|
@ -185,7 +196,7 @@ class UserSourceConnection(CreatedUpdatedModel):
|
|||
|
||||
class Meta:
|
||||
|
||||
unique_together = (('user', 'source'),)
|
||||
unique_together = (("user", "source"),)
|
||||
|
||||
|
||||
class Policy(UUIDModel, CreatedUpdatedModel):
|
||||
|
@ -215,25 +226,25 @@ class DebugPolicy(Policy):
|
|||
wait_min = models.IntegerField(default=5)
|
||||
wait_max = models.IntegerField(default=30)
|
||||
|
||||
form = 'passbook.core.forms.policies.DebugPolicyForm'
|
||||
form = "passbook.core.forms.policies.DebugPolicyForm"
|
||||
|
||||
def passes(self, request: PolicyRequest) -> PolicyResult:
|
||||
"""Wait random time then return result"""
|
||||
wait = SystemRandom().randrange(self.wait_min, self.wait_max)
|
||||
LOGGER.debug("Policy waiting", policy=self, delay=wait)
|
||||
sleep(wait)
|
||||
return PolicyResult(self.result, 'Debugging')
|
||||
return PolicyResult(self.result, "Debugging")
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('Debug Policy')
|
||||
verbose_name_plural = _('Debug Policies')
|
||||
verbose_name = _("Debug Policy")
|
||||
verbose_name_plural = _("Debug Policies")
|
||||
|
||||
|
||||
class Invitation(UUIDModel):
|
||||
"""Single-use invitation link"""
|
||||
|
||||
created_by = models.ForeignKey('User', on_delete=models.CASCADE)
|
||||
created_by = models.ForeignKey("User", on_delete=models.CASCADE)
|
||||
expires = models.DateTimeField(default=None, blank=True, null=True)
|
||||
fixed_username = models.TextField(blank=True, default=None)
|
||||
fixed_email = models.TextField(blank=True, default=None)
|
||||
|
@ -242,24 +253,26 @@ class Invitation(UUIDModel):
|
|||
@property
|
||||
def link(self):
|
||||
"""Get link to use invitation"""
|
||||
return reverse_lazy('passbook_core:auth-sign-up') + f'?invitation={self.uuid.hex}'
|
||||
return (
|
||||
reverse_lazy("passbook_core:auth-sign-up") + f"?invitation={self.uuid.hex}"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"Invitation {self.uuid.hex} created by {self.created_by}"
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('Invitation')
|
||||
verbose_name_plural = _('Invitations')
|
||||
verbose_name = _("Invitation")
|
||||
verbose_name_plural = _("Invitations")
|
||||
|
||||
|
||||
class Nonce(UUIDModel):
|
||||
"""One-time link for password resets/sign-up-confirmations"""
|
||||
|
||||
expires = models.DateTimeField(default=default_nonce_duration)
|
||||
user = models.ForeignKey('User', on_delete=models.CASCADE)
|
||||
user = models.ForeignKey("User", on_delete=models.CASCADE)
|
||||
expiring = models.BooleanField(default=True)
|
||||
description = models.TextField(default='', blank=True)
|
||||
description = models.TextField(default="", blank=True)
|
||||
|
||||
@property
|
||||
def is_expired(self) -> bool:
|
||||
|
@ -271,8 +284,8 @@ class Nonce(UUIDModel):
|
|||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('Nonce')
|
||||
verbose_name_plural = _('Nonces')
|
||||
verbose_name = _("Nonce")
|
||||
verbose_name_plural = _("Nonces")
|
||||
|
||||
|
||||
class PropertyMapping(UUIDModel):
|
||||
|
@ -280,7 +293,7 @@ class PropertyMapping(UUIDModel):
|
|||
|
||||
name = models.TextField()
|
||||
|
||||
form = ''
|
||||
form = ""
|
||||
objects = InheritanceManager()
|
||||
|
||||
def __str__(self):
|
||||
|
@ -288,5 +301,5 @@ class PropertyMapping(UUIDModel):
|
|||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('Property Mapping')
|
||||
verbose_name_plural = _('Property Mappings')
|
||||
verbose_name = _("Property Mapping")
|
||||
verbose_name_plural = _("Property Mappings")
|
||||
|
|
|
@ -7,10 +7,10 @@ from structlog import get_logger
|
|||
|
||||
LOGGER = get_logger()
|
||||
|
||||
user_signed_up = Signal(providing_args=['request', 'user'])
|
||||
invitation_created = Signal(providing_args=['request', 'invitation'])
|
||||
invitation_used = Signal(providing_args=['request', 'invitation', 'user'])
|
||||
password_changed = Signal(providing_args=['user', 'password'])
|
||||
user_signed_up = Signal(providing_args=["request", "user"])
|
||||
invitation_created = Signal(providing_args=["request", "invitation"])
|
||||
invitation_used = Signal(providing_args=["request", "invitation", "user"])
|
||||
password_changed = Signal(providing_args=["user", "password"])
|
||||
|
||||
|
||||
@receiver(post_save)
|
||||
|
@ -18,6 +18,7 @@ password_changed = Signal(providing_args=['user', 'password'])
|
|||
def invalidate_policy_cache(sender, instance, **_):
|
||||
"""Invalidate Policy cache when policy is updated"""
|
||||
from passbook.core.models import Policy
|
||||
|
||||
if isinstance(instance, Policy):
|
||||
LOGGER.debug("Invalidating policy cache", policy=instance)
|
||||
keys = cache.keys("%s#*" % instance.pk)
|
||||
|
|
|
@ -7,8 +7,9 @@ from passbook.root.celery import CELERY_APP
|
|||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
@CELERY_APP.task()
|
||||
def clean_nonces():
|
||||
"""Remove expired nonces"""
|
||||
amount, _ = Nonce.objects.filter(expires__lt=now(), expiring=True).delete()
|
||||
LOGGER.debug('Deleted expired nonces', amount=amount)
|
||||
LOGGER.debug("Deleted expired nonces", amount=amount)
|
||||
|
|
|
@ -9,29 +9,37 @@ from passbook.policies.engine import PolicyEngine
|
|||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def user_factors(context: RequestContext) -> List[UserSettings]:
|
||||
"""Return list of all factors which apply to user"""
|
||||
user = context.get('request').user
|
||||
_all_factors = Factor.objects.filter(enabled=True).order_by('order').select_subclasses()
|
||||
user = context.get("request").user
|
||||
_all_factors = (
|
||||
Factor.objects.filter(enabled=True).order_by("order").select_subclasses()
|
||||
)
|
||||
matching_factors: List[UserSettings] = []
|
||||
for factor in _all_factors:
|
||||
user_settings = factor.user_settings()
|
||||
policy_engine = PolicyEngine(factor.policies.all(), user, context.get('request'))
|
||||
policy_engine = PolicyEngine(
|
||||
factor.policies.all(), user, context.get("request")
|
||||
)
|
||||
policy_engine.build()
|
||||
if policy_engine.passing and user_settings:
|
||||
matching_factors.append(user_settings)
|
||||
return matching_factors
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def user_sources(context: RequestContext) -> List[UserSettings]:
|
||||
"""Return a list of all sources which are enabled for the user"""
|
||||
user = context.get('request').user
|
||||
user = context.get("request").user
|
||||
_all_sources = Source.objects.filter(enabled=True).select_subclasses()
|
||||
matching_sources: List[UserSettings] = []
|
||||
for factor in _all_sources:
|
||||
user_settings = factor.user_settings()
|
||||
policy_engine = PolicyEngine(factor.policies.all(), user, context.get('request'))
|
||||
policy_engine = PolicyEngine(
|
||||
factor.policies.all(), user, context.get("request")
|
||||
)
|
||||
policy_engine.build()
|
||||
if policy_engine.passing and user_settings:
|
||||
matching_sources.append(user_settings)
|
||||
|
|
|
@ -15,70 +15,78 @@ class TestAuthenticationViews(TestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.sign_up_data = {
|
||||
'name': 'Test',
|
||||
'username': 'beryjuorg',
|
||||
'email': 'unittest@passbook.beryju.org',
|
||||
'password': 'B3ryju0rg!',
|
||||
'password_repeat': 'B3ryju0rg!',
|
||||
"name": "Test",
|
||||
"username": "beryjuorg",
|
||||
"email": "unittest@passbook.beryju.org",
|
||||
"password": "B3ryju0rg!",
|
||||
"password_repeat": "B3ryju0rg!",
|
||||
}
|
||||
self.login_data = {
|
||||
'uid_field': 'unittest@example.com',
|
||||
"uid_field": "unittest@example.com",
|
||||
}
|
||||
self.user = User.objects.create_superuser(
|
||||
username='unittest user',
|
||||
email='unittest@example.com',
|
||||
password=''.join(SystemRandom().choice(
|
||||
string.ascii_uppercase + string.digits) for _ in range(8)))
|
||||
|
||||
username="unittest user",
|
||||
email="unittest@example.com",
|
||||
password="".join(
|
||||
SystemRandom().choice(string.ascii_uppercase + string.digits)
|
||||
for _ in range(8)
|
||||
),
|
||||
)
|
||||
|
||||
def test_sign_up_view(self):
|
||||
"""Test account.sign_up view (Anonymous)"""
|
||||
self.client.logout()
|
||||
response = self.client.get(reverse('passbook_core:auth-sign-up'))
|
||||
response = self.client.get(reverse("passbook_core:auth-sign-up"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_login_view(self):
|
||||
"""Test account.login view (Anonymous)"""
|
||||
self.client.logout()
|
||||
response = self.client.get(reverse('passbook_core:auth-login'))
|
||||
response = self.client.get(reverse("passbook_core:auth-login"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# test login with post
|
||||
form = LoginForm(self.login_data)
|
||||
self.assertTrue(form.is_valid())
|
||||
|
||||
response = self.client.post(reverse('passbook_core:auth-login'), data=form.cleaned_data)
|
||||
response = self.client.post(
|
||||
reverse("passbook_core:auth-login"), data=form.cleaned_data
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_logout_view(self):
|
||||
"""Test account.logout view"""
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(reverse('passbook_core:auth-logout'))
|
||||
response = self.client.get(reverse("passbook_core:auth-logout"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_sign_up_view_auth(self):
|
||||
"""Test account.sign_up view (Authenticated)"""
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(reverse('passbook_core:auth-logout'))
|
||||
response = self.client.get(reverse("passbook_core:auth-logout"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_login_view_auth(self):
|
||||
"""Test account.login view (Authenticated)"""
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(reverse('passbook_core:auth-login'))
|
||||
response = self.client.get(reverse("passbook_core:auth-login"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_login_view_post(self):
|
||||
"""Test account.login view POST (Anonymous)"""
|
||||
login_response = self.client.post(reverse('passbook_core:auth-login'), data=self.login_data)
|
||||
login_response = self.client.post(
|
||||
reverse("passbook_core:auth-login"), data=self.login_data
|
||||
)
|
||||
self.assertEqual(login_response.status_code, 302)
|
||||
self.assertEqual(login_response.url, reverse('passbook_core:auth-process'))
|
||||
self.assertEqual(login_response.url, reverse("passbook_core:auth-process"))
|
||||
|
||||
def test_sign_up_view_post(self):
|
||||
"""Test account.sign_up view POST (Anonymous)"""
|
||||
form = SignUpForm(self.sign_up_data)
|
||||
self.assertTrue(form.is_valid())
|
||||
|
||||
response = self.client.post(reverse('passbook_core:auth-sign-up'), data=form.cleaned_data)
|
||||
response = self.client.post(
|
||||
reverse("passbook_core:auth-sign-up"), data=form.cleaned_data
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# def test_reset_password_init_view(self):
|
||||
|
|
|
@ -14,12 +14,17 @@ class TestOverviewViews(TestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.user = User.objects.create_superuser(
|
||||
username='unittest user',
|
||||
email='unittest@example.com',
|
||||
password=''.join(SystemRandom().choice(
|
||||
string.ascii_uppercase + string.digits) for _ in range(8)))
|
||||
username="unittest user",
|
||||
email="unittest@example.com",
|
||||
password="".join(
|
||||
SystemRandom().choice(string.ascii_uppercase + string.digits)
|
||||
for _ in range(8)
|
||||
),
|
||||
)
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def test_overview(self):
|
||||
"""Test UserSettingsView"""
|
||||
self.assertEqual(self.client.get(reverse('passbook_core:overview')).status_code, 200)
|
||||
self.assertEqual(
|
||||
self.client.get(reverse("passbook_core:overview")).status_code, 200
|
||||
)
|
||||
|
|
|
@ -15,33 +15,43 @@ class TestUserViews(TestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.user = User.objects.create_superuser(
|
||||
username='unittest user',
|
||||
email='unittest@example.com',
|
||||
password=''.join(SystemRandom().choice(
|
||||
string.ascii_uppercase + string.digits) for _ in range(8)))
|
||||
username="unittest user",
|
||||
email="unittest@example.com",
|
||||
password="".join(
|
||||
SystemRandom().choice(string.ascii_uppercase + string.digits)
|
||||
for _ in range(8)
|
||||
),
|
||||
)
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def test_user_settings(self):
|
||||
"""Test UserSettingsView"""
|
||||
self.assertEqual(self.client.get(reverse('passbook_core:user-settings')).status_code, 200)
|
||||
self.assertEqual(
|
||||
self.client.get(reverse("passbook_core:user-settings")).status_code, 200
|
||||
)
|
||||
|
||||
def test_user_delete(self):
|
||||
"""Test UserDeleteView"""
|
||||
self.assertEqual(self.client.post(reverse('passbook_core:user-delete')).status_code, 302)
|
||||
self.assertEqual(User.objects.filter(username='unittest user').exists(), False)
|
||||
self.assertEqual(
|
||||
self.client.post(reverse("passbook_core:user-delete")).status_code, 302
|
||||
)
|
||||
self.assertEqual(User.objects.filter(username="unittest user").exists(), False)
|
||||
self.setUp()
|
||||
|
||||
def test_user_change_password(self):
|
||||
"""Test UserChangePasswordView"""
|
||||
form_data = {
|
||||
'password': 'test2',
|
||||
'password_repeat': 'test2'
|
||||
}
|
||||
form_data = {"password": "test2", "password_repeat": "test2"}
|
||||
form = PasswordChangeForm(data=form_data)
|
||||
self.assertTrue(form.is_valid())
|
||||
self.assertEqual(self.client.get(
|
||||
reverse('passbook_core:user-change-password')).status_code, 200)
|
||||
self.assertEqual(self.client.post(
|
||||
reverse('passbook_core:user-change-password'), data=form_data).status_code, 302)
|
||||
self.assertEqual(
|
||||
self.client.get(reverse("passbook_core:user-change-password")).status_code,
|
||||
200,
|
||||
)
|
||||
self.assertEqual(
|
||||
self.client.post(
|
||||
reverse("passbook_core:user-change-password"), data=form_data
|
||||
).status_code,
|
||||
302,
|
||||
)
|
||||
self.user.refresh_from_db()
|
||||
self.assertTrue(self.user.check_password('test2'))
|
||||
self.assertTrue(self.user.check_password("test2"))
|
||||
|
|
|
@ -13,22 +13,25 @@ class TestUtilViews(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_superuser(
|
||||
username='unittest user',
|
||||
email='unittest@example.com',
|
||||
password=''.join(SystemRandom().choice(
|
||||
string.ascii_uppercase + string.digits) for _ in range(8)))
|
||||
username="unittest user",
|
||||
email="unittest@example.com",
|
||||
password="".join(
|
||||
SystemRandom().choice(string.ascii_uppercase + string.digits)
|
||||
for _ in range(8)
|
||||
),
|
||||
)
|
||||
self.factory = RequestFactory()
|
||||
|
||||
def test_loading_view(self):
|
||||
"""Test loading view"""
|
||||
request = self.factory.get('something')
|
||||
response = LoadingView.as_view(target_url='somestring')(request)
|
||||
request = self.factory.get("something")
|
||||
response = LoadingView.as_view(target_url="somestring")(request)
|
||||
response.render()
|
||||
self.assertIn('somestring', response.content.decode('utf-8'))
|
||||
self.assertIn("somestring", response.content.decode("utf-8"))
|
||||
|
||||
def test_permission_denied_view(self):
|
||||
"""Test PermissionDeniedView"""
|
||||
request = self.factory.get('something')
|
||||
request = self.factory.get("something")
|
||||
request.user = self.user
|
||||
response = PermissionDeniedView.as_view()(request)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
|
@ -9,21 +9,38 @@ LOGGER = get_logger()
|
|||
|
||||
urlpatterns = [
|
||||
# Authentication views
|
||||
path('auth/login/', authentication.LoginView.as_view(), name='auth-login'),
|
||||
path('auth/logout/', authentication.LogoutView.as_view(), name='auth-logout'),
|
||||
path('auth/sign_up/', authentication.SignUpView.as_view(), name='auth-sign-up'),
|
||||
path('auth/sign_up/<uuid:nonce>/confirm/', authentication.SignUpConfirmView.as_view(),
|
||||
name='auth-sign-up-confirm'),
|
||||
path('auth/process/denied/', view.FactorPermissionDeniedView.as_view(), name='auth-denied'),
|
||||
path('auth/password/reset/<uuid:nonce>/', authentication.PasswordResetView.as_view(),
|
||||
name='auth-password-reset'),
|
||||
path('auth/process/', view.AuthenticationView.as_view(), name='auth-process'),
|
||||
path('auth/process/<slug:factor>/', view.AuthenticationView.as_view(), name='auth-process'),
|
||||
path("auth/login/", authentication.LoginView.as_view(), name="auth-login"),
|
||||
path("auth/logout/", authentication.LogoutView.as_view(), name="auth-logout"),
|
||||
path("auth/sign_up/", authentication.SignUpView.as_view(), name="auth-sign-up"),
|
||||
path(
|
||||
"auth/sign_up/<uuid:nonce>/confirm/",
|
||||
authentication.SignUpConfirmView.as_view(),
|
||||
name="auth-sign-up-confirm",
|
||||
),
|
||||
path(
|
||||
"auth/process/denied/",
|
||||
view.FactorPermissionDeniedView.as_view(),
|
||||
name="auth-denied",
|
||||
),
|
||||
path(
|
||||
"auth/password/reset/<uuid:nonce>/",
|
||||
authentication.PasswordResetView.as_view(),
|
||||
name="auth-password-reset",
|
||||
),
|
||||
path("auth/process/", view.AuthenticationView.as_view(), name="auth-process"),
|
||||
path(
|
||||
"auth/process/<slug:factor>/",
|
||||
view.AuthenticationView.as_view(),
|
||||
name="auth-process",
|
||||
),
|
||||
# User views
|
||||
path('_/user/', user.UserSettingsView.as_view(), name='user-settings'),
|
||||
path('_/user/delete/', user.UserDeleteView.as_view(), name='user-delete'),
|
||||
path('_/user/change_password/', user.UserChangePasswordView.as_view(),
|
||||
name='user-change-password'),
|
||||
path("_/user/", user.UserSettingsView.as_view(), name="user-settings"),
|
||||
path("_/user/delete/", user.UserDeleteView.as_view(), name="user-delete"),
|
||||
path(
|
||||
"_/user/change_password/",
|
||||
user.UserChangePasswordView.as_view(),
|
||||
name="user-change-password",
|
||||
),
|
||||
# Overview
|
||||
path('', overview.OverviewView.as_view(), name='overview'),
|
||||
path("", overview.OverviewView.as_view(), name="overview"),
|
||||
]
|
||||
|
|
|
@ -11,6 +11,7 @@ from passbook.policies.engine import PolicyEngine
|
|||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class AccessMixin:
|
||||
"""Mixin class for usage in Authorization views.
|
||||
Provider functions to check application access, etc"""
|
||||
|
@ -23,12 +24,18 @@ class AccessMixin:
|
|||
try:
|
||||
return provider.application
|
||||
except Application.DoesNotExist as exc:
|
||||
messages.error(self.request, _('Provider "%(name)s" has no application assigned' % {
|
||||
'name': provider
|
||||
}))
|
||||
messages.error(
|
||||
self.request,
|
||||
_(
|
||||
'Provider "%(name)s" has no application assigned'
|
||||
% {"name": provider}
|
||||
),
|
||||
)
|
||||
raise exc
|
||||
|
||||
def user_has_access(self, application: Application, user: User) -> Tuple[bool, List[str]]:
|
||||
def user_has_access(
|
||||
self, application: Application, user: User
|
||||
) -> Tuple[bool, List[str]]:
|
||||
"""Check if user has access to application."""
|
||||
LOGGER.debug("Checking permissions", user=user, application=application)
|
||||
policy_engine = PolicyEngine(application.policies.all(), user, self.request)
|
||||
|
|
|
@ -25,41 +25,41 @@ LOGGER = get_logger()
|
|||
class LoginView(UserPassesTestMixin, FormView):
|
||||
"""Allow users to sign in"""
|
||||
|
||||
template_name = 'login/form.html'
|
||||
template_name = "login/form.html"
|
||||
form_class = LoginForm
|
||||
success_url = '.'
|
||||
success_url = "."
|
||||
|
||||
# Allow only not authenticated users to login
|
||||
def test_func(self):
|
||||
return self.request.user.is_authenticated is False
|
||||
|
||||
def handle_no_permission(self):
|
||||
if 'next' in self.request.GET:
|
||||
return redirect(self.request.GET.get('next'))
|
||||
return redirect(reverse('passbook_core:overview'))
|
||||
if "next" in self.request.GET:
|
||||
return redirect(self.request.GET.get("next"))
|
||||
return redirect(reverse("passbook_core:overview"))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['config'] = CONFIG.y('passbook')
|
||||
kwargs['is_login'] = True
|
||||
kwargs['title'] = _('Log in to your account')
|
||||
kwargs['primary_action'] = _('Log in')
|
||||
kwargs['show_sign_up_notice'] = CONFIG.y('passbook.sign_up.enabled')
|
||||
kwargs['sources'] = []
|
||||
kwargs["config"] = CONFIG.y("passbook")
|
||||
kwargs["is_login"] = True
|
||||
kwargs["title"] = _("Log in to your account")
|
||||
kwargs["primary_action"] = _("Log in")
|
||||
kwargs["show_sign_up_notice"] = CONFIG.y("passbook.sign_up.enabled")
|
||||
kwargs["sources"] = []
|
||||
sources = Source.objects.filter(enabled=True).select_subclasses()
|
||||
for source in sources:
|
||||
login_button = source.login_button
|
||||
if login_button:
|
||||
kwargs['sources'].append(login_button)
|
||||
if kwargs['sources']:
|
||||
self.template_name = 'login/with_sources.html'
|
||||
kwargs["sources"].append(login_button)
|
||||
if kwargs["sources"]:
|
||||
self.template_name = "login/with_sources.html"
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_user(self, uid_value) -> Optional[User]:
|
||||
"""Find user instance. Returns None if no user was found."""
|
||||
for search_field in CONFIG.y('passbook.uid_fields'):
|
||||
for search_field in CONFIG.y("passbook.uid_fields"):
|
||||
# Workaround for E-Mail -> email
|
||||
if search_field == 'e-mail':
|
||||
search_field = 'email'
|
||||
if search_field == "e-mail":
|
||||
search_field = "email"
|
||||
users = User.objects.filter(**{search_field: uid_value})
|
||||
if users.exists():
|
||||
LOGGER.debug("Found user", user=users.first(), uid_field=search_field)
|
||||
|
@ -68,18 +68,20 @@ class LoginView(UserPassesTestMixin, FormView):
|
|||
|
||||
def form_valid(self, form: LoginForm) -> HttpResponse:
|
||||
"""Form data is valid"""
|
||||
pre_user = self.get_user(form.cleaned_data.get('uid_field'))
|
||||
pre_user = self.get_user(form.cleaned_data.get("uid_field"))
|
||||
if not pre_user:
|
||||
# No user found
|
||||
return self.invalid_login(self.request)
|
||||
# self.request.session.flush()
|
||||
self.request.session[AuthenticationView.SESSION_PENDING_USER] = pre_user.pk
|
||||
return _redirect_with_qs('passbook_core:auth-process', self.request.GET)
|
||||
return _redirect_with_qs("passbook_core:auth-process", self.request.GET)
|
||||
|
||||
def invalid_login(self, request: HttpRequest, disabled_user: User = None) -> HttpResponse:
|
||||
def invalid_login(
|
||||
self, request: HttpRequest, disabled_user: User = None
|
||||
) -> HttpResponse:
|
||||
"""Handle login for disabled users/invalid login attempts"""
|
||||
LOGGER.debug("invalid_login", user=disabled_user)
|
||||
messages.error(request, _('Failed to authenticate.'))
|
||||
messages.error(request, _("Failed to authenticate."))
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
|
||||
|
@ -90,15 +92,15 @@ class LogoutView(LoginRequiredMixin, View):
|
|||
"""Log current user out"""
|
||||
logout(request)
|
||||
messages.success(request, _("You've successfully been logged out."))
|
||||
return redirect(reverse('passbook_core:auth-login'))
|
||||
return redirect(reverse("passbook_core:auth-login"))
|
||||
|
||||
|
||||
class SignUpView(UserPassesTestMixin, FormView):
|
||||
"""Sign up new user, optionally consume one-use invitation link."""
|
||||
|
||||
template_name = 'login/form.html'
|
||||
template_name = "login/form.html"
|
||||
form_class = SignUpForm
|
||||
success_url = '.'
|
||||
success_url = "."
|
||||
# Invitation instance, if invitation link was used
|
||||
_invitation = None
|
||||
# Instance of newly created user
|
||||
|
@ -109,38 +111,38 @@ class SignUpView(UserPassesTestMixin, FormView):
|
|||
return self.request.user.is_authenticated is False
|
||||
|
||||
def handle_no_permission(self):
|
||||
return redirect(reverse('passbook_core:overview'))
|
||||
return redirect(reverse("passbook_core:overview"))
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""Check if sign-up is enabled or invitation link given"""
|
||||
allowed = False
|
||||
if 'invitation' in request.GET:
|
||||
invitations = Invitation.objects.filter(uuid=request.GET.get('invitation'))
|
||||
if "invitation" in request.GET:
|
||||
invitations = Invitation.objects.filter(uuid=request.GET.get("invitation"))
|
||||
allowed = invitations.exists()
|
||||
if allowed:
|
||||
self._invitation = invitations.first()
|
||||
if CONFIG.y('passbook.sign_up.enabled'):
|
||||
if CONFIG.y("passbook.sign_up.enabled"):
|
||||
allowed = True
|
||||
if not allowed:
|
||||
messages.error(request, _('Sign-ups are currently disabled.'))
|
||||
return redirect(reverse('passbook_core:auth-login'))
|
||||
messages.error(request, _("Sign-ups are currently disabled."))
|
||||
return redirect(reverse("passbook_core:auth-login"))
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_initial(self):
|
||||
if self._invitation:
|
||||
initial = {}
|
||||
if self._invitation.fixed_username:
|
||||
initial['username'] = self._invitation.fixed_username
|
||||
initial["username"] = self._invitation.fixed_username
|
||||
if self._invitation.fixed_email:
|
||||
initial['email'] = self._invitation.fixed_email
|
||||
initial["email"] = self._invitation.fixed_email
|
||||
return initial
|
||||
return super().get_initial()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['config'] = CONFIG.y('passbook')
|
||||
kwargs['is_login'] = True
|
||||
kwargs['title'] = _('Sign Up')
|
||||
kwargs['primary_action'] = _('Sign up')
|
||||
kwargs["config"] = CONFIG.y("passbook")
|
||||
kwargs["is_login"] = True
|
||||
kwargs["title"] = _("Sign Up")
|
||||
kwargs["primary_action"] = _("Sign up")
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def form_valid(self, form: SignUpForm) -> HttpResponse:
|
||||
|
@ -173,9 +175,8 @@ class SignUpView(UserPassesTestMixin, FormView):
|
|||
# self._user.save()
|
||||
self.consume_invitation()
|
||||
messages.success(self.request, _("Successfully signed up!"))
|
||||
LOGGER.debug("Successfully signed up %s",
|
||||
form.cleaned_data.get('email'))
|
||||
return redirect(reverse('passbook_core:auth-login'))
|
||||
LOGGER.debug("Successfully signed up %s", form.cleaned_data.get("email"))
|
||||
return redirect(reverse("passbook_core:auth-login"))
|
||||
|
||||
def consume_invitation(self):
|
||||
"""Consume invitation if an invitation was used"""
|
||||
|
@ -184,7 +185,8 @@ class SignUpView(UserPassesTestMixin, FormView):
|
|||
sender=self,
|
||||
request=self.request,
|
||||
invitation=self._invitation,
|
||||
user=self._user)
|
||||
user=self._user,
|
||||
)
|
||||
self._invitation.delete()
|
||||
|
||||
@staticmethod
|
||||
|
@ -204,20 +206,17 @@ class SignUpView(UserPassesTestMixin, FormView):
|
|||
"""
|
||||
# Create user
|
||||
new_user = User.objects.create(
|
||||
username=data.get('username'),
|
||||
email=data.get('email'),
|
||||
name=data.get('name'),
|
||||
username=data.get("username"),
|
||||
email=data.get("email"),
|
||||
name=data.get("name"),
|
||||
)
|
||||
new_user.is_active = True
|
||||
try:
|
||||
new_user.set_password(data.get('password'))
|
||||
new_user.set_password(data.get("password"))
|
||||
new_user.save()
|
||||
request.user = new_user
|
||||
# Send signal for other auth sources
|
||||
user_signed_up.send(
|
||||
sender=SignUpView,
|
||||
user=new_user,
|
||||
request=request)
|
||||
user_signed_up.send(sender=SignUpView, user=new_user, request=request)
|
||||
return new_user
|
||||
except PasswordPolicyInvalid as exc:
|
||||
new_user.delete()
|
||||
|
@ -233,11 +232,11 @@ class SignUpConfirmView(View):
|
|||
nonce.user.is_active = True
|
||||
nonce.user.save()
|
||||
# Workaround: hardcoded reference to ModelBackend, needs testing
|
||||
nonce.user.backend = 'django.contrib.auth.backends.ModelBackend'
|
||||
nonce.user.backend = "django.contrib.auth.backends.ModelBackend"
|
||||
login(request, nonce.user)
|
||||
nonce.delete()
|
||||
messages.success(request, _('Successfully confirmed registration.'))
|
||||
return redirect('passbook_core:overview')
|
||||
messages.success(request, _("Successfully confirmed registration."))
|
||||
return redirect("passbook_core:overview")
|
||||
|
||||
|
||||
class PasswordResetView(View):
|
||||
|
@ -248,9 +247,11 @@ class PasswordResetView(View):
|
|||
# 3. (Optional) Trap user in password change view
|
||||
nonce = get_object_or_404(Nonce, uuid=nonce)
|
||||
# Workaround: hardcoded reference to ModelBackend, needs testing
|
||||
nonce.user.backend = 'django.contrib.auth.backends.ModelBackend'
|
||||
nonce.user.backend = "django.contrib.auth.backends.ModelBackend"
|
||||
login(request, nonce.user)
|
||||
nonce.delete()
|
||||
messages.success(request, _(('Temporarily authenticated with Nonce, '
|
||||
'please change your password')))
|
||||
return redirect('passbook_core:user-change-password')
|
||||
messages.success(
|
||||
request,
|
||||
_(("Temporarily authenticated with Nonce, " "please change your password")),
|
||||
)
|
||||
return redirect("passbook_core:user-change-password")
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
"""passbook core error views"""
|
||||
|
||||
from django.http.response import (HttpResponseBadRequest,
|
||||
HttpResponseForbidden, HttpResponseNotFound,
|
||||
HttpResponseServerError)
|
||||
from django.http.response import (
|
||||
HttpResponseBadRequest,
|
||||
HttpResponseForbidden,
|
||||
HttpResponseNotFound,
|
||||
HttpResponseServerError,
|
||||
)
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
@ -10,54 +13,53 @@ from django.views.generic import TemplateView
|
|||
class BadRequestTemplateResponse(TemplateResponse, HttpResponseBadRequest):
|
||||
"""Combine Template response with Http Code 400"""
|
||||
|
||||
|
||||
class ForbiddenTemplateResponse(TemplateResponse, HttpResponseForbidden):
|
||||
"""Combine Template response with Http Code 403"""
|
||||
|
||||
|
||||
class NotFoundTemplateResponse(TemplateResponse, HttpResponseNotFound):
|
||||
"""Combine Template response with Http Code 404"""
|
||||
|
||||
|
||||
class ServerErrorTemplateResponse(TemplateResponse, HttpResponseServerError):
|
||||
"""Combine Template response with Http Code 500"""
|
||||
|
||||
|
||||
class BadRequestView(TemplateView):
|
||||
"""Show Bad Request message"""
|
||||
|
||||
response_class = BadRequestTemplateResponse
|
||||
template_name = 'error/400.html'
|
||||
template_name = "error/400.html"
|
||||
|
||||
extra_context = {"is_login": True}
|
||||
|
||||
extra_context = {
|
||||
'is_login': True
|
||||
}
|
||||
|
||||
class ForbiddenView(TemplateView):
|
||||
"""Show Forbidden message"""
|
||||
|
||||
response_class = ForbiddenTemplateResponse
|
||||
template_name = 'error/403.html'
|
||||
template_name = "error/403.html"
|
||||
|
||||
extra_context = {"is_login": True}
|
||||
|
||||
extra_context = {
|
||||
'is_login': True
|
||||
}
|
||||
|
||||
class NotFoundView(TemplateView):
|
||||
"""Show Not Found message"""
|
||||
|
||||
response_class = NotFoundTemplateResponse
|
||||
template_name = 'error/404.html'
|
||||
template_name = "error/404.html"
|
||||
|
||||
extra_context = {"is_login": True}
|
||||
|
||||
extra_context = {
|
||||
'is_login': True
|
||||
}
|
||||
|
||||
class ServerErrorView(TemplateView):
|
||||
"""Show Server Error message"""
|
||||
|
||||
response_class = ServerErrorTemplateResponse
|
||||
template_name = 'error/500.html'
|
||||
template_name = "error/500.html"
|
||||
|
||||
extra_context = {
|
||||
'is_login': True
|
||||
}
|
||||
extra_context = {"is_login": True}
|
||||
|
||||
# pylint: disable=useless-super-delegation
|
||||
def dispatch(self, *args, **kwargs):
|
||||
|
|
|
@ -11,13 +11,15 @@ class OverviewView(LoginRequiredMixin, TemplateView):
|
|||
"""Overview for logged in user, incase user opens passbook directly
|
||||
and is not being forwarded"""
|
||||
|
||||
template_name = 'overview/index.html'
|
||||
template_name = "overview/index.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['applications'] = []
|
||||
kwargs["applications"] = []
|
||||
for application in Application.objects.all():
|
||||
engine = PolicyEngine(application.policies.all(), self.request.user, self.request)
|
||||
engine = PolicyEngine(
|
||||
application.policies.all(), self.request.user, self.request
|
||||
)
|
||||
engine.build()
|
||||
if engine.passing:
|
||||
kwargs['applications'].append(application)
|
||||
kwargs["applications"].append(application)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -17,11 +17,11 @@ from passbook.lib.config import CONFIG
|
|||
class UserSettingsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
|
||||
"""Update User settings"""
|
||||
|
||||
template_name = 'user/settings.html'
|
||||
template_name = "user/settings.html"
|
||||
form_class = UserDetailForm
|
||||
|
||||
success_message = _('Successfully updated user.')
|
||||
success_url = reverse_lazy('passbook_core:user-settings')
|
||||
success_message = _("Successfully updated user.")
|
||||
success_url = reverse_lazy("passbook_core:user-settings")
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user
|
||||
|
@ -30,44 +30,44 @@ class UserSettingsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
|
|||
class UserDeleteView(LoginRequiredMixin, DeleteView):
|
||||
"""Delete user account"""
|
||||
|
||||
template_name = 'generic/delete.html'
|
||||
template_name = "generic/delete.html"
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user
|
||||
|
||||
def get_success_url(self):
|
||||
messages.success(self.request, _('Successfully deleted user.'))
|
||||
messages.success(self.request, _("Successfully deleted user."))
|
||||
logout(self.request)
|
||||
return reverse('passbook_core:auth-login')
|
||||
return reverse("passbook_core:auth-login")
|
||||
|
||||
|
||||
class UserChangePasswordView(LoginRequiredMixin, FormView):
|
||||
"""View for users to update their password"""
|
||||
|
||||
form_class = PasswordChangeForm
|
||||
template_name = 'login/form_with_user.html'
|
||||
template_name = "login/form_with_user.html"
|
||||
|
||||
def form_valid(self, form: PasswordChangeForm):
|
||||
try:
|
||||
# user.set_password checks against Policies so we don't need to manually do it here
|
||||
self.request.user.set_password(form.cleaned_data.get('password'))
|
||||
self.request.user.set_password(form.cleaned_data.get("password"))
|
||||
self.request.user.save()
|
||||
update_session_auth_hash(self.request, self.request.user)
|
||||
messages.success(self.request, _('Successfully changed password'))
|
||||
messages.success(self.request, _("Successfully changed password"))
|
||||
except PasswordPolicyInvalid as exc:
|
||||
# Manually inject error into form
|
||||
# pylint: disable=protected-access
|
||||
errors = form._errors.setdefault("password_repeat", ErrorList(''))
|
||||
errors = form._errors.setdefault("password_repeat", ErrorList(""))
|
||||
# pylint: disable=protected-access
|
||||
errors = form._errors.setdefault("password", ErrorList())
|
||||
for error in exc.messages:
|
||||
errors.append(error)
|
||||
return self.form_invalid(form)
|
||||
return redirect('passbook_core:overview')
|
||||
return redirect("passbook_core:overview")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['config'] = CONFIG.y('passbook')
|
||||
kwargs['is_login'] = True
|
||||
kwargs['title'] = _('Change Password')
|
||||
kwargs['primary_action'] = _('Change')
|
||||
kwargs["config"] = CONFIG.y("passbook")
|
||||
kwargs["is_login"] = True
|
||||
kwargs["title"] = _("Change Password")
|
||||
kwargs["primary_action"] = _("Change")
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -6,8 +6,8 @@ from django.views.generic import TemplateView
|
|||
class LoadingView(TemplateView):
|
||||
"""View showing a loading template, and forwarding to real view using html forwarding."""
|
||||
|
||||
template_name = 'login/loading.html'
|
||||
title = _('Loading')
|
||||
template_name = "login/loading.html"
|
||||
title = _("Loading")
|
||||
target_url = None
|
||||
|
||||
def get_url(self):
|
||||
|
@ -15,18 +15,19 @@ class LoadingView(TemplateView):
|
|||
return self.target_url
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['is_login'] = True
|
||||
kwargs['title'] = self.title
|
||||
kwargs['target_url'] = self.get_url()
|
||||
kwargs["is_login"] = True
|
||||
kwargs["title"] = self.title
|
||||
kwargs["target_url"] = self.get_url()
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class PermissionDeniedView(TemplateView):
|
||||
"""Generic Permission denied view"""
|
||||
|
||||
template_name = 'login/denied.html'
|
||||
title = _('Permission denied.')
|
||||
template_name = "login/denied.html"
|
||||
title = _("Permission denied.")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['is_login'] = True
|
||||
kwargs['title'] = self.title
|
||||
kwargs["is_login"] = True
|
||||
kwargs["title"] = self.title
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -17,16 +17,16 @@ class AuthenticationFactor(TemplateView):
|
|||
authenticator: AuthenticationView
|
||||
pending_user: User
|
||||
request: HttpRequest = None
|
||||
template_name = 'login/form_with_user.html'
|
||||
template_name = "login/form_with_user.html"
|
||||
|
||||
def __init__(self, authenticator: AuthenticationView):
|
||||
self.authenticator = authenticator
|
||||
self.pending_user = None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['config'] = CONFIG.y('passbook')
|
||||
kwargs['is_login'] = True
|
||||
kwargs['title'] = _('Log in to your account')
|
||||
kwargs['primary_action'] = _('Log in')
|
||||
kwargs['user'] = self.pending_user
|
||||
kwargs["config"] = CONFIG.y("passbook")
|
||||
kwargs["is_login"] = True
|
||||
kwargs["title"] = _("Log in to your account")
|
||||
kwargs["primary_action"] = _("Log in")
|
||||
kwargs["user"] = self.pending_user
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
from passbook.lib.admin import admin_autoregister
|
||||
|
||||
admin_autoregister('passbook_factors_captcha')
|
||||
admin_autoregister("passbook_factors_captcha")
|
||||
|
|
|
@ -11,7 +11,7 @@ class CaptchaFactorSerializer(ModelSerializer):
|
|||
class Meta:
|
||||
|
||||
model = CaptchaFactor
|
||||
fields = ['pk', 'name', 'slug', 'order', 'enabled', 'public_key', 'private_key']
|
||||
fields = ["pk", "name", "slug", "order", "enabled", "public_key", "private_key"]
|
||||
|
||||
|
||||
class CaptchaFactorViewSet(ModelViewSet):
|
||||
|
|
|
@ -5,6 +5,6 @@ from django.apps import AppConfig
|
|||
class PassbookFactorCaptchaConfig(AppConfig):
|
||||
"""passbook captcha app"""
|
||||
|
||||
name = 'passbook.factors.captcha'
|
||||
label = 'passbook_factors_captcha'
|
||||
verbose_name = 'passbook Factors.Captcha'
|
||||
name = "passbook.factors.captcha"
|
||||
label = "passbook_factors_captcha"
|
||||
verbose_name = "passbook Factors.Captcha"
|
||||
|
|
|
@ -16,7 +16,11 @@ class CaptchaFactor(FormView, AuthenticationFactor):
|
|||
|
||||
def get_form(self, form_class=None):
|
||||
form = CaptchaForm(**self.get_form_kwargs())
|
||||
form.fields['captcha'].public_key = self.authenticator.current_factor.public_key
|
||||
form.fields['captcha'].private_key = self.authenticator.current_factor.private_key
|
||||
form.fields['captcha'].widget.attrs["data-sitekey"] = form.fields['captcha'].public_key
|
||||
form.fields["captcha"].public_key = self.authenticator.current_factor.public_key
|
||||
form.fields[
|
||||
"captcha"
|
||||
].private_key = self.authenticator.current_factor.private_key
|
||||
form.fields["captcha"].widget.attrs["data-sitekey"] = form.fields[
|
||||
"captcha"
|
||||
].public_key
|
||||
return form
|
||||
|
|
|
@ -13,17 +13,18 @@ class CaptchaForm(forms.Form):
|
|||
|
||||
captcha = ReCaptchaField()
|
||||
|
||||
|
||||
class CaptchaFactorForm(forms.ModelForm):
|
||||
"""Form to edit CaptchaFactor Instance"""
|
||||
|
||||
class Meta:
|
||||
|
||||
model = CaptchaFactor
|
||||
fields = GENERAL_FIELDS + ['public_key', 'private_key']
|
||||
fields = GENERAL_FIELDS + ["public_key", "private_key"]
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
'order': forms.NumberInput(),
|
||||
'policies': FilteredSelectMultiple(_('policies'), False),
|
||||
'public_key': forms.TextInput(),
|
||||
'private_key': forms.TextInput(),
|
||||
"name": forms.TextInput(),
|
||||
"order": forms.NumberInput(),
|
||||
"policies": FilteredSelectMultiple(_("policies"), False),
|
||||
"public_key": forms.TextInput(),
|
||||
"private_key": forms.TextInput(),
|
||||
}
|
||||
|
|
|
@ -9,21 +9,31 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0001_initial'),
|
||||
("passbook_core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CaptchaFactor',
|
||||
name="CaptchaFactor",
|
||||
fields=[
|
||||
('factor_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Factor')),
|
||||
('public_key', models.TextField()),
|
||||
('private_key', models.TextField()),
|
||||
(
|
||||
"factor_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_core.Factor",
|
||||
),
|
||||
),
|
||||
("public_key", models.TextField()),
|
||||
("private_key", models.TextField()),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Captcha Factor',
|
||||
'verbose_name_plural': 'Captcha Factors',
|
||||
"verbose_name": "Captcha Factor",
|
||||
"verbose_name_plural": "Captcha Factors",
|
||||
},
|
||||
bases=('passbook_core.factor',),
|
||||
bases=("passbook_core.factor",),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -11,13 +11,13 @@ class CaptchaFactor(Factor):
|
|||
public_key = models.TextField()
|
||||
private_key = models.TextField()
|
||||
|
||||
type = 'passbook.factors.captcha.factor.CaptchaFactor'
|
||||
form = 'passbook.factors.captcha.forms.CaptchaFactorForm'
|
||||
type = "passbook.factors.captcha.factor.CaptchaFactor"
|
||||
form = "passbook.factors.captcha.forms.CaptchaFactorForm"
|
||||
|
||||
def __str__(self):
|
||||
return f"Captcha Factor {self.slug}"
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('Captcha Factor')
|
||||
verbose_name_plural = _('Captcha Factors')
|
||||
verbose_name = _("Captcha Factor")
|
||||
verbose_name_plural = _("Captcha Factors")
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
"""passbook captcha_factor settings"""
|
||||
# https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do
|
||||
RECAPTCHA_PUBLIC_KEY = '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'
|
||||
RECAPTCHA_PRIVATE_KEY = '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe'
|
||||
RECAPTCHA_PUBLIC_KEY = "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
|
||||
RECAPTCHA_PRIVATE_KEY = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
|
||||
|
||||
NOCAPTCHA = True
|
||||
INSTALLED_APPS = [
|
||||
'captcha'
|
||||
]
|
||||
SILENCED_SYSTEM_CHECKS = ['captcha.recaptcha_test_key_error']
|
||||
INSTALLED_APPS = ["captcha"]
|
||||
SILENCED_SYSTEM_CHECKS = ["captcha.recaptcha_test_key_error"]
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
from passbook.lib.admin import admin_autoregister
|
||||
|
||||
admin_autoregister('passbook_factors_dummy')
|
||||
admin_autoregister("passbook_factors_dummy")
|
||||
|
|
|
@ -11,7 +11,7 @@ class DummyFactorSerializer(ModelSerializer):
|
|||
class Meta:
|
||||
|
||||
model = DummyFactor
|
||||
fields = ['pk', 'name', 'slug', 'order', 'enabled']
|
||||
fields = ["pk", "name", "slug", "order", "enabled"]
|
||||
|
||||
|
||||
class DummyFactorViewSet(ModelViewSet):
|
||||
|
|
|
@ -6,6 +6,6 @@ from django.apps import AppConfig
|
|||
class PassbookFactorDummyConfig(AppConfig):
|
||||
"""passbook dummy factor config"""
|
||||
|
||||
name = 'passbook.factors.dummy'
|
||||
label = 'passbook_factors_dummy'
|
||||
verbose_name = 'passbook Factors.Dummy'
|
||||
name = "passbook.factors.dummy"
|
||||
label = "passbook_factors_dummy"
|
||||
verbose_name = "passbook Factors.Dummy"
|
||||
|
|
|
@ -15,7 +15,7 @@ class DummyFactorForm(forms.ModelForm):
|
|||
model = DummyFactor
|
||||
fields = GENERAL_FIELDS
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
'order': forms.NumberInput(),
|
||||
'policies': FilteredSelectMultiple(_('policies'), False)
|
||||
"name": forms.TextInput(),
|
||||
"order": forms.NumberInput(),
|
||||
"policies": FilteredSelectMultiple(_("policies"), False),
|
||||
}
|
||||
|
|
|
@ -9,19 +9,29 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0001_initial'),
|
||||
("passbook_core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DummyFactor',
|
||||
name="DummyFactor",
|
||||
fields=[
|
||||
('factor_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Factor')),
|
||||
(
|
||||
"factor_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_core.Factor",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Dummy Factor',
|
||||
'verbose_name_plural': 'Dummy Factors',
|
||||
"verbose_name": "Dummy Factor",
|
||||
"verbose_name_plural": "Dummy Factors",
|
||||
},
|
||||
bases=('passbook_core.factor',),
|
||||
bases=("passbook_core.factor",),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,13 +7,13 @@ from passbook.core.models import Factor
|
|||
class DummyFactor(Factor):
|
||||
"""Dummy factor, mostly used to debug"""
|
||||
|
||||
type = 'passbook.factors.dummy.factor.DummyFactor'
|
||||
form = 'passbook.factors.dummy.forms.DummyFactorForm'
|
||||
type = "passbook.factors.dummy.factor.DummyFactor"
|
||||
form = "passbook.factors.dummy.forms.DummyFactorForm"
|
||||
|
||||
def __str__(self):
|
||||
return f"Dummy Factor {self.slug}"
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('Dummy Factor')
|
||||
verbose_name_plural = _('Dummy Factors')
|
||||
verbose_name = _("Dummy Factor")
|
||||
verbose_name_plural = _("Dummy Factors")
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
from passbook.lib.admin import admin_autoregister
|
||||
|
||||
admin_autoregister('passbook_factors_email')
|
||||
admin_autoregister("passbook_factors_email")
|
||||
|
|
|
@ -11,19 +11,24 @@ class EmailFactorSerializer(ModelSerializer):
|
|||
class Meta:
|
||||
|
||||
model = EmailFactor
|
||||
fields = ['pk', 'name', 'slug', 'order', 'enabled', 'host',
|
||||
'port',
|
||||
'username',
|
||||
'password',
|
||||
'use_tls',
|
||||
'use_ssl',
|
||||
'timeout',
|
||||
'from_address',
|
||||
'ssl_keyfile',
|
||||
'ssl_certfile', ]
|
||||
extra_kwargs = {
|
||||
'password': {'write_only': True}
|
||||
}
|
||||
fields = [
|
||||
"pk",
|
||||
"name",
|
||||
"slug",
|
||||
"order",
|
||||
"enabled",
|
||||
"host",
|
||||
"port",
|
||||
"username",
|
||||
"password",
|
||||
"use_tls",
|
||||
"use_ssl",
|
||||
"timeout",
|
||||
"from_address",
|
||||
"ssl_keyfile",
|
||||
"ssl_certfile",
|
||||
]
|
||||
extra_kwargs = {"password": {"write_only": True}}
|
||||
|
||||
|
||||
class EmailFactorViewSet(ModelViewSet):
|
||||
|
|
|
@ -7,9 +7,9 @@ from django.apps import AppConfig
|
|||
class PassbookFactorEmailConfig(AppConfig):
|
||||
"""passbook email factor config"""
|
||||
|
||||
name = 'passbook.factors.email'
|
||||
label = 'passbook_factors_email'
|
||||
verbose_name = 'passbook Factors.Email'
|
||||
name = "passbook.factors.email"
|
||||
label = "passbook_factors_email"
|
||||
verbose_name = "passbook Factors.Email"
|
||||
|
||||
def ready(self):
|
||||
import_module('passbook.factors.email.tasks')
|
||||
import_module("passbook.factors.email.tasks")
|
||||
|
|
|
@ -18,27 +18,31 @@ class EmailFactorView(AuthenticationFactor):
|
|||
"""Dummy factor for testing with multiple factors"""
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['show_password_forget_notice'] = CONFIG.y('passbook.password_reset.enabled')
|
||||
kwargs["show_password_forget_notice"] = CONFIG.y(
|
||||
"passbook.password_reset.enabled"
|
||||
)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
nonce = Nonce.objects.create(user=self.pending_user)
|
||||
# Send mail to user
|
||||
message = TemplateEmailMessage(
|
||||
subject=_('Forgotten password'),
|
||||
template_name='email/account_password_reset.html',
|
||||
subject=_("Forgotten password"),
|
||||
template_name="email/account_password_reset.html",
|
||||
to=[self.pending_user.email],
|
||||
template_context={
|
||||
'url': self.request.build_absolute_uri(
|
||||
reverse('passbook_core:auth-password-reset',
|
||||
kwargs={
|
||||
'nonce': nonce.uuid
|
||||
})
|
||||
)})
|
||||
"url": self.request.build_absolute_uri(
|
||||
reverse(
|
||||
"passbook_core:auth-password-reset",
|
||||
kwargs={"nonce": nonce.uuid},
|
||||
)
|
||||
)
|
||||
},
|
||||
)
|
||||
send_mails(self.authenticator.current_factor, message)
|
||||
self.authenticator.cleanup()
|
||||
messages.success(request, _('Check your E-Mails for a password reset link.'))
|
||||
return redirect('passbook_core:auth-login')
|
||||
messages.success(request, _("Check your E-Mails for a password reset link."))
|
||||
return redirect("passbook_core:auth-login")
|
||||
|
||||
def post(self, request: HttpRequest):
|
||||
"""Just redirect to next factor"""
|
||||
|
|
|
@ -14,30 +14,30 @@ class EmailFactorForm(forms.ModelForm):
|
|||
|
||||
model = EmailFactor
|
||||
fields = GENERAL_FIELDS + [
|
||||
'host',
|
||||
'port',
|
||||
'username',
|
||||
'password',
|
||||
'use_tls',
|
||||
'use_ssl',
|
||||
'timeout',
|
||||
'from_address',
|
||||
'ssl_keyfile',
|
||||
'ssl_certfile',
|
||||
"host",
|
||||
"port",
|
||||
"username",
|
||||
"password",
|
||||
"use_tls",
|
||||
"use_ssl",
|
||||
"timeout",
|
||||
"from_address",
|
||||
"ssl_keyfile",
|
||||
"ssl_certfile",
|
||||
]
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
'order': forms.NumberInput(),
|
||||
'policies': FilteredSelectMultiple(_('policies'), False),
|
||||
'host': forms.TextInput(),
|
||||
'username': forms.TextInput(),
|
||||
'password': forms.TextInput(),
|
||||
'ssl_keyfile': forms.TextInput(),
|
||||
'ssl_certfile': forms.TextInput(),
|
||||
"name": forms.TextInput(),
|
||||
"order": forms.NumberInput(),
|
||||
"policies": FilteredSelectMultiple(_("policies"), False),
|
||||
"host": forms.TextInput(),
|
||||
"username": forms.TextInput(),
|
||||
"password": forms.TextInput(),
|
||||
"ssl_keyfile": forms.TextInput(),
|
||||
"ssl_certfile": forms.TextInput(),
|
||||
}
|
||||
labels = {
|
||||
'use_tls': _('Use TLS'),
|
||||
'use_ssl': _('Use SSL'),
|
||||
'ssl_keyfile': _('SSL Keyfile (optional)'),
|
||||
'ssl_certfile': _('SSL Certfile (optional)'),
|
||||
"use_tls": _("Use TLS"),
|
||||
"use_ssl": _("Use SSL"),
|
||||
"ssl_keyfile": _("SSL Keyfile (optional)"),
|
||||
"ssl_certfile": _("SSL Certfile (optional)"),
|
||||
}
|
||||
|
|
|
@ -9,29 +9,42 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0001_initial'),
|
||||
("passbook_core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EmailFactor',
|
||||
name="EmailFactor",
|
||||
fields=[
|
||||
('factor_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Factor')),
|
||||
('host', models.TextField(default='localhost')),
|
||||
('port', models.IntegerField(default=25)),
|
||||
('username', models.TextField(blank=True, default='')),
|
||||
('password', models.TextField(blank=True, default='')),
|
||||
('use_tls', models.BooleanField(default=False)),
|
||||
('use_ssl', models.BooleanField(default=False)),
|
||||
('timeout', models.IntegerField(default=0)),
|
||||
('ssl_keyfile', models.TextField(blank=True, default=None, null=True)),
|
||||
('ssl_certfile', models.TextField(blank=True, default=None, null=True)),
|
||||
('from_address', models.EmailField(default='system@passbook.local', max_length=254)),
|
||||
(
|
||||
"factor_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_core.Factor",
|
||||
),
|
||||
),
|
||||
("host", models.TextField(default="localhost")),
|
||||
("port", models.IntegerField(default=25)),
|
||||
("username", models.TextField(blank=True, default="")),
|
||||
("password", models.TextField(blank=True, default="")),
|
||||
("use_tls", models.BooleanField(default=False)),
|
||||
("use_ssl", models.BooleanField(default=False)),
|
||||
("timeout", models.IntegerField(default=0)),
|
||||
("ssl_keyfile", models.TextField(blank=True, default=None, null=True)),
|
||||
("ssl_certfile", models.TextField(blank=True, default=None, null=True)),
|
||||
(
|
||||
"from_address",
|
||||
models.EmailField(default="system@passbook.local", max_length=254),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Email Factor',
|
||||
'verbose_name_plural': 'Email Factors',
|
||||
"verbose_name": "Email Factor",
|
||||
"verbose_name_plural": "Email Factors",
|
||||
},
|
||||
bases=('passbook_core.factor',),
|
||||
bases=("passbook_core.factor",),
|
||||
),
|
||||
]
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue