diff --git a/helm/passbook/templates/configmap.yaml b/helm/passbook/templates/configmap.yaml
index 6ee203ae6..1e2a81bed 100644
--- a/helm/passbook/templates/configmap.yaml
+++ b/helm/passbook/templates/configmap.yaml
@@ -90,23 +90,9 @@ data:
# create_users: true
# # Reset LDAP password when user reset their password
# reset_password: true
- oauth_client:
- # List of python packages with sources types to load.
- types:
- - passbook.oauth_client.source_types.discord
- - passbook.oauth_client.source_types.facebook
- - passbook.oauth_client.source_types.github
- - passbook.oauth_client.source_types.google
- - passbook.oauth_client.source_types.reddit
- - passbook.oauth_client.source_types.supervisr
- - passbook.oauth_client.source_types.twitter
- - passbook.oauth_client.source_types.azure_ad
saml_idp:
signing: true
autosubmit: false
issuer: passbook
assertion_valid_for: 86400
# List of python packages with provider types to load.
- types:
- - passbook.saml_idp.processors.generic
- - passbook.saml_idp.processors.salesforce
diff --git a/helm/passbook/templates/worker-deployment.yaml b/helm/passbook/templates/worker-deployment.yaml
index 1164e65b3..409fc757d 100644
--- a/helm/passbook/templates/worker-deployment.yaml
+++ b/helm/passbook/templates/worker-deployment.yaml
@@ -29,9 +29,13 @@ spec:
image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}"
imagePullPolicy: IfNotPresent
command:
- - ./manage.py
+ - celery
args:
- worker
+ - --autoscale=10,3
+ - -E
+ - -B
+ - -A passbook.root.celery
envFrom:
- configMapRef:
name: {{ include "passbook.fullname" . }}-config
diff --git a/passbook/admin/views/overview.py b/passbook/admin/views/overview.py
index c7ffd41e4..679792828 100644
--- a/passbook/admin/views/overview.py
+++ b/passbook/admin/views/overview.py
@@ -3,8 +3,8 @@ from django.core.cache import cache
from django.shortcuts import redirect, reverse
from django.views.generic import TemplateView
+from passbook import __version__
from passbook.admin.mixins import AdminRequiredMixin
-from passbook.core import __version__
from passbook.core.models import (Application, Factor, Invitation, Policy,
Provider, Source, User)
from passbook.root.celery import CELERY_APP
diff --git a/passbook/admin/views/policy.py b/passbook/admin/views/policy.py
index 355717126..541d70e09 100644
--- a/passbook/admin/views/policy.py
+++ b/passbook/admin/views/policy.py
@@ -12,7 +12,7 @@ from passbook.admin.forms.policies import PolicyTestForm
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import Policy
from passbook.lib.utils.reflection import path_to_class
-from passbook.policy.engine import PolicyEngine
+from passbook.policies.engine import PolicyEngine
class PolicyListView(AdminRequiredMixin, ListView):
diff --git a/passbook/app_gw/.DS_Store b/passbook/app_gw/.DS_Store
deleted file mode 100644
index 45dfba929..000000000
Binary files a/passbook/app_gw/.DS_Store and /dev/null differ
diff --git a/passbook/app_gw/apps.py b/passbook/app_gw/apps.py
deleted file mode 100644
index b3c8cec2b..000000000
--- a/passbook/app_gw/apps.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""passbook Application Security Gateway app"""
-from django.apps import AppConfig
-
-
-class PassbookApplicationApplicationGatewayConfig(AppConfig):
- """passbook app_gw app"""
-
- name = 'passbook.app_gw'
- label = 'passbook_app_gw'
- verbose_name = 'passbook Application Security Gateway'
- # mountpoint = 'app_gw/'
diff --git a/passbook/app_gw/migrations/.DS_Store b/passbook/app_gw/migrations/.DS_Store
deleted file mode 100644
index 5008ddfcf..000000000
Binary files a/passbook/app_gw/migrations/.DS_Store and /dev/null differ
diff --git a/passbook/app_gw/migrations/0002_auto_20190321_1521.py b/passbook/app_gw/migrations/0002_auto_20190321_1521.py
deleted file mode 100644
index 3a9dbe301..000000000
--- a/passbook/app_gw/migrations/0002_auto_20190321_1521.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-21 15:21
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_app_gw', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='rewriterule',
- name='conditions',
- field=models.ManyToManyField(blank=True, to='passbook_core.Policy'),
- ),
- ]
diff --git a/passbook/app_gw/migrations/0003_auto_20190411_1314.py b/passbook/app_gw/migrations/0003_auto_20190411_1314.py
deleted file mode 100644
index 28434b016..000000000
--- a/passbook/app_gw/migrations/0003_auto_20190411_1314.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2019-04-11 13:14
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_app_gw', '0002_auto_20190321_1521'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='applicationgatewayprovider',
- name='authentication_header',
- field=models.TextField(blank=True, default='X-Remote-User'),
- ),
- ]
diff --git a/passbook/audit/migrations/0001_initial.py b/passbook/audit/migrations/0001_initial.py
index 568b3d7d7..32d1bd814 100644
--- a/passbook/audit/migrations/0001_initial.py
+++ b/passbook/audit/migrations/0001_initial.py
@@ -1,7 +1,8 @@
-# Generated by Django 2.1.7 on 2019-02-16 09:13
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import uuid
+import django.contrib.postgres.fields.jsonb
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
@@ -23,7 +24,7 @@ class Migration(migrations.Migration):
('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', 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)),
@@ -33,19 +34,4 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Audit Entries',
},
),
- migrations.CreateModel(
- name='LoginAttempt',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('created', models.DateField(auto_now_add=True)),
- ('last_updated', models.DateTimeField(auto_now=True)),
- ('target_uid', models.CharField(max_length=254)),
- ('request_ip', models.GenericIPAddressField()),
- ('attempts', models.IntegerField(default=1)),
- ],
- ),
- migrations.AlterUniqueTogether(
- name='loginattempt',
- unique_together={('target_uid', 'request_ip', 'created')},
- ),
]
diff --git a/passbook/audit/migrations/0002_auto_20190221_1201.py b/passbook/audit/migrations/0002_auto_20190221_1201.py
deleted file mode 100644
index 2327a386d..000000000
--- a/passbook/audit/migrations/0002_auto_20190221_1201.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-21 12:01
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_audit', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='loginattempt',
- name='created',
- field=models.DateTimeField(auto_now_add=True),
- ),
- ]
diff --git a/passbook/audit/migrations/0003_auto_20190221_1240.py b/passbook/audit/migrations/0003_auto_20190221_1240.py
deleted file mode 100644
index d8e0a87f6..000000000
--- a/passbook/audit/migrations/0003_auto_20190221_1240.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-21 12:40
-
-import django.contrib.postgres.fields.jsonb
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_audit', '0002_auto_20190221_1201'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='auditentry',
- name='_context',
- ),
- migrations.AddField(
- model_name='auditentry',
- name='context',
- field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict),
- ),
- ]
diff --git a/passbook/audit/migrations/0004_delete_loginattempt.py b/passbook/audit/migrations/0004_delete_loginattempt.py
deleted file mode 100644
index 96ca0e8dc..000000000
--- a/passbook/audit/migrations/0004_delete_loginattempt.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-08 14:53
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_audit', '0003_auto_20190221_1240'),
- ]
-
- operations = [
- migrations.DeleteModel(
- name='LoginAttempt',
- ),
- ]
diff --git a/passbook/captcha_factor/apps.py b/passbook/captcha_factor/apps.py
deleted file mode 100644
index 8c1c3294e..000000000
--- a/passbook/captcha_factor/apps.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""passbook captcha app"""
-from django.apps import AppConfig
-
-
-class PassbookCaptchaFactorConfig(AppConfig):
- """passbook captcha app"""
-
- name = 'passbook.captcha_factor'
- label = 'passbook_captcha_factor'
- verbose_name = 'passbook Captcha'
diff --git a/passbook/core/__init__.py b/passbook/core/__init__.py
index fdd22d84b..e69de29bb 100644
--- a/passbook/core/__init__.py
+++ b/passbook/core/__init__.py
@@ -1,2 +0,0 @@
-"""passbook core"""
-__version__ = '0.2.6-beta'
diff --git a/passbook/core/apps.py b/passbook/core/apps.py
index cc38e1fb6..70c0e791d 100644
--- a/passbook/core/apps.py
+++ b/passbook/core/apps.py
@@ -2,12 +2,12 @@
from importlib import import_module
from django.apps import AppConfig
+from django.conf import settings
from structlog import get_logger
-from passbook.lib.config import CONFIG
-
LOGGER = get_logger()
+
class PassbookCoreConfig(AppConfig):
"""passbook core app config"""
@@ -17,9 +17,7 @@ class PassbookCoreConfig(AppConfig):
mountpoint = ''
def ready(self):
- import_module('passbook.policy.engine')
- factors_to_load = CONFIG.y('passbook.factors', [])
- for factors_to_load in factors_to_load:
+ for factors_to_load in settings.PASSBOOK_CORE_FACTORS:
try:
import_module(factors_to_load)
LOGGER.info("Loaded factor", factor_class=factors_to_load)
diff --git a/passbook/core/forms/applications.py b/passbook/core/forms/applications.py
index 254c999e6..812cf724b 100644
--- a/passbook/core/forms/applications.py
+++ b/passbook/core/forms/applications.py
@@ -16,7 +16,7 @@ class ApplicationForm(forms.ModelForm):
model = Application
fields = ['name', 'slug', 'launch_url', 'icon_url',
- 'policies', 'provider', 'skip_authorization']
+ 'provider', 'policies', 'skip_authorization']
widgets = {
'name': forms.TextInput(),
'launch_url': forms.TextInput(),
diff --git a/passbook/core/forms/policies.py b/passbook/core/forms/policies.py
index 7a3293c58..d8ca19127 100644
--- a/passbook/core/forms/policies.py
+++ b/passbook/core/forms/policies.py
@@ -3,40 +3,8 @@
from django import forms
from django.utils.translation import gettext as _
-from passbook.core.models import (DebugPolicy, FieldMatcherPolicy,
- GroupMembershipPolicy, PasswordPolicy,
- SSOLoginPolicy, WebhookPolicy)
-
-GENERAL_FIELDS = ['name', 'action', 'negate', 'order', 'timeout']
-
-class FieldMatcherPolicyForm(forms.ModelForm):
- """FieldMatcherPolicy Form"""
-
- class Meta:
-
- model = FieldMatcherPolicy
- fields = GENERAL_FIELDS + ['user_field', 'match_action', 'value', ]
- widgets = {
- 'name': forms.TextInput(),
- 'value': forms.TextInput(),
- }
-
-
-class WebhookPolicyForm(forms.ModelForm):
- """WebhookPolicyForm Form"""
-
- class Meta:
-
- model = WebhookPolicy
- fields = GENERAL_FIELDS + ['url', 'method', 'json_body', 'json_headers',
- 'result_jsonpath', 'result_json_value', ]
- widgets = {
- 'name': forms.TextInput(),
- 'json_body': forms.TextInput(),
- 'json_headers': forms.TextInput(),
- 'result_jsonpath': forms.TextInput(),
- 'result_json_value': forms.TextInput(),
- }
+from passbook.core.models import DebugPolicy
+from passbook.policies.forms import GENERAL_FIELDS
class DebugPolicyForm(forms.ModelForm):
@@ -52,49 +20,3 @@ class DebugPolicyForm(forms.ModelForm):
labels = {
'result': _('Allow user')
}
-
-
-class GroupMembershipPolicyForm(forms.ModelForm):
- """GroupMembershipPolicy Form"""
-
- class Meta:
-
- model = GroupMembershipPolicy
- fields = GENERAL_FIELDS + ['group', ]
- widgets = {
- 'name': forms.TextInput(),
- 'order': forms.NumberInput(),
- }
-
-class SSOLoginPolicyForm(forms.ModelForm):
- """Edit SSOLoginPolicy instances"""
-
- class Meta:
-
- model = SSOLoginPolicy
- fields = GENERAL_FIELDS
- widgets = {
- 'name': forms.TextInput(),
- 'order': forms.NumberInput(),
- }
-
-class PasswordPolicyForm(forms.ModelForm):
- """PasswordPolicy Form"""
-
- class Meta:
-
- model = PasswordPolicy
- fields = GENERAL_FIELDS + ['amount_uppercase', 'amount_lowercase',
- 'amount_symbols', 'length_min', 'symbol_charset',
- 'error_message']
- widgets = {
- 'name': forms.TextInput(),
- 'symbol_charset': forms.TextInput(),
- 'error_message': forms.TextInput(),
- }
- labels = {
- 'amount_uppercase': _('Minimum amount of Uppercase Characters'),
- 'amount_lowercase': _('Minimum amount of Lowercase Characters'),
- 'amount_symbols': _('Minimum amount of Symbols Characters'),
- 'length_min': _('Minimum Length'),
- }
diff --git a/passbook/core/management/commands/import_users.py b/passbook/core/management/commands/import_users.py
deleted file mode 100644
index 889c5e5d7..000000000
--- a/passbook/core/management/commands/import_users.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""passbook import_users management command"""
-from csv import DictReader
-
-from django.core.management.base import BaseCommand
-from django.core.validators import EmailValidator, ValidationError
-from structlog import get_logger
-
-from passbook.core.models import User
-
-LOGGER = get_logger()
-
-class Command(BaseCommand):
- """Import users from CSV file"""
-
- def add_arguments(self, parser):
- # Positional arguments
- parser.add_argument('file', nargs='+', type=str)
-
- def handle(self, *args, **options):
- """Create Users from CSV file"""
- for file in options.get('file'):
- with open(file, 'r') as _file:
- reader = DictReader(_file)
- for user in reader:
- LOGGER.debug('User %s', user.get('username'))
- try:
- # only import users with valid email addresses
- if user.get('email'):
- validator = EmailValidator()
- validator(user.get('email'))
- # use combination of username and email to check for existing user
- if User.objects.filter(
- username=user.get('username'),
- email=user.get('email')).exists():
- LOGGER.debug('User %s exists already, skipping', user.get('username'))
- # Create user
- User.objects.create(
- username=user.get('username'),
- email=user.get('email'),
- name=user.get('name'),
- password=user.get('password'))
- LOGGER.debug('Created User %s', user.get('username'))
- except ValidationError as exc:
- LOGGER.warning('User %s caused %r, skipping', user.get('username'), exc)
- continue
diff --git a/passbook/core/management/commands/web.py b/passbook/core/management/commands/web.py
deleted file mode 100644
index 34e4f69e0..000000000
--- a/passbook/core/management/commands/web.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""passbook Webserver management command"""
-
-import cherrypy
-from django.conf import settings
-from django.core.management.base import BaseCommand
-from structlog import get_logger
-
-from passbook.lib.config import CONFIG
-from passbook.root.wsgi import application
-
-LOGGER = get_logger()
-
-
-class Command(BaseCommand):
- """Run CherryPy webserver"""
-
- def handle(self, *args, **options):
- """passbook cherrypy server"""
- cherrypy.config.update(CONFIG.y('web'))
- cherrypy.tree.graft(application, '/')
- # Mount NullObject to serve static files
- cherrypy.tree.mount(None, settings.STATIC_URL, config={
- '/': {
- 'tools.staticdir.on': True,
- 'tools.staticdir.dir': settings.STATIC_ROOT,
- 'tools.expires.on': True,
- 'tools.expires.secs': 86400,
- 'tools.gzip.on': True,
- }
- })
- cherrypy.engine.start()
- for file in CONFIG.loaded_file:
- cherrypy.engine.autoreload.files.add(file)
- LOGGER.info("Added '%s' to autoreload triggers", file)
- cherrypy.engine.block()
diff --git a/passbook/core/management/commands/worker.py b/passbook/core/management/commands/worker.py
deleted file mode 100644
index cb11f59ea..000000000
--- a/passbook/core/management/commands/worker.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""passbook Worker management command"""
-
-from django.core.management.base import BaseCommand
-from django.utils import autoreload
-from structlog import get_logger
-
-from passbook.root.celery import CELERY_APP
-
-LOGGER = get_logger()
-
-
-class Command(BaseCommand):
- """Run Celery Worker"""
-
- def handle(self, *args, **options):
- """celery worker"""
- autoreload.run_with_reloader(self.celery_worker)
-
- def celery_worker(self):
- """Run celery worker within autoreload"""
- autoreload.raise_last_exception()
- CELERY_APP.worker_main(['worker', '--autoscale=10,3', '-E', '-B'])
diff --git a/passbook/core/migrations/0001_initial.py b/passbook/core/migrations/0001_initial.py
index 94e90987e..bbc44d1dc 100644
--- a/passbook/core/migrations/0001_initial.py
+++ b/passbook/core/migrations/0001_initial.py
@@ -1,21 +1,24 @@
-# Generated by Django 2.1.7 on 2019-02-16 09:10
+# Generated by Django 2.2.6 on 2019-10-07 14:06
import uuid
import django.contrib.auth.models
import django.contrib.auth.validators
+import django.contrib.postgres.fields.jsonb
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
+import passbook.core.models
+
class Migration(migrations.Migration):
initial = True
dependencies = [
- ('auth', '0009_alter_user_last_name_max_length'),
+ ('auth', '0011_update_proxy_permissions'),
]
operations = [
@@ -34,6 +37,8 @@ class Migration(migrations.Migration):
('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',
@@ -44,39 +49,17 @@ class Migration(migrations.Migration):
('objects', django.contrib.auth.models.UserManager()),
],
),
- 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')),
- ('extra_data', models.TextField(blank=True)),
- ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='passbook_core.Group')),
- ],
- ),
- 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)),
- ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- options={
- 'verbose_name': 'Invitation',
- 'verbose_name_plural': 'Invitations',
- },
- ),
migrations.CreateModel(
name='Policy',
fields=[
- ('created', models.DateField(auto_now_add=True)),
+ ('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,
@@ -85,28 +68,136 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='PolicyModel',
fields=[
- ('created', models.DateField(auto_now_add=True)),
+ ('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={
+ 'verbose_name': 'Property Mapping',
+ 'verbose_name_plural': 'Property Mappings',
+ },
+ ),
+ migrations.CreateModel(
+ name='DebugPolicy',
+ 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)),
+ ],
+ options={
+ 'verbose_name': 'Debug Policy',
+ 'verbose_name_plural': 'Debug Policies',
+ },
+ bases=('passbook_core.policy',),
+ ),
+ migrations.CreateModel(
+ name='Factor',
+ 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')),
],
),
+ migrations.CreateModel(
+ 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)),
+ ],
+ 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': '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')},
+ },
+ ),
+ migrations.AddField(
+ 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'),
+ ),
migrations.CreateModel(
name='UserSourceConnection',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('created', models.DateField(auto_now_add=True)),
+ ('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')},
+ },
),
migrations.CreateModel(
name='Application',
@@ -124,131 +215,9 @@ class Migration(migrations.Migration):
},
bases=('passbook_core.policymodel',),
),
- migrations.CreateModel(
- name='DebugPolicy',
- 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)),
- ],
- options={
- 'verbose_name': 'Debug Policy',
- 'verbose_name_plural': 'Debug Policys',
- },
- bases=('passbook_core.policy',),
- ),
- migrations.CreateModel(
- name='Factor',
- 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()),
- ('type', models.TextField(unique=True)),
- ('enabled', models.BooleanField(default=True)),
- ],
- options={
- 'abstract': False,
- },
- bases=('passbook_core.policymodel',),
- ),
- migrations.CreateModel(
- name='FieldMatcherPolicy',
- 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')),
- ('user_field', models.TextField(choices=[('username', 'Username'), ('first_name', 'First Name'), ('last_name', 'Last Name'), ('email', 'E-Mail'), ('is_staff', 'Is staff'), ('is_active', 'Is active'), ('data_joined', 'Date joined')])),
- ('match_action', models.CharField(choices=[('startswith', 'Starts with'), ('endswith', 'Ends with'), ('endswith', 'Contains'), ('regexp', 'Regexp'), ('exact', 'Exact')], max_length=50)),
- ('value', models.TextField()),
- ],
- options={
- 'verbose_name': 'Field matcher Policy',
- 'verbose_name_plural': 'Field matcher Policys',
- },
- bases=('passbook_core.policy',),
- ),
- migrations.CreateModel(
- name='PasswordPolicyPolicy',
- 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')),
- ('amount_uppercase', models.IntegerField(default=0)),
- ('amount_lowercase', models.IntegerField(default=0)),
- ('amount_symbols', models.IntegerField(default=0)),
- ('length_min', models.IntegerField(default=0)),
- ('symbol_charset', models.TextField(default='!\\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ')),
- ],
- options={
- 'verbose_name': 'Password Policy Policy',
- 'verbose_name_plural': 'Password Policy Policys',
- },
- bases=('passbook_core.policy',),
- ),
- 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='WebhookPolicy',
- 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')),
- ('url', models.URLField()),
- ('method', models.CharField(choices=[('GET', 'GET'), ('POST', 'POST'), ('PATCH', 'PATCH'), ('DELETE', 'DELETE'), ('PUT', 'PUT')], max_length=10)),
- ('json_body', models.TextField()),
- ('json_headers', models.TextField()),
- ('result_jsonpath', models.TextField()),
- ('result_json_value', models.TextField()),
- ],
- options={
- 'verbose_name': 'Webhook Policy',
- 'verbose_name_plural': 'Webhook Policys',
- },
- bases=('passbook_core.policy',),
- ),
- migrations.AddField(
- model_name='policymodel',
- name='policies',
- field=models.ManyToManyField(blank=True, to='passbook_core.Policy'),
- ),
- migrations.AddField(
- 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'),
- ),
- migrations.AddField(
- model_name='usersourceconnection',
- name='source',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='passbook_core.Source'),
- ),
- migrations.AlterUniqueTogether(
- name='group',
- unique_together={('name', 'parent')},
- ),
- migrations.AddField(
- model_name='user',
- name='applications',
- field=models.ManyToManyField(to='passbook_core.Application'),
- ),
migrations.AddField(
model_name='user',
name='sources',
field=models.ManyToManyField(through='passbook_core.UserSourceConnection', to='passbook_core.Source'),
),
- migrations.AlterUniqueTogether(
- name='usersourceconnection',
- unique_together={('user', 'source')},
- ),
]
diff --git a/passbook/core/migrations/0002_auto_20190216_1002.py b/passbook/core/migrations/0002_auto_20190216_1002.py
deleted file mode 100644
index 09704b03a..000000000
--- a/passbook/core/migrations/0002_auto_20190216_1002.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-16 10:02
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='debugpolicy',
- options={'verbose_name': 'Debug Policy', 'verbose_name_plural': 'Debug Policies'},
- ),
- migrations.AlterModelOptions(
- name='fieldmatcherpolicy',
- options={'verbose_name': 'Field matcher Policy', 'verbose_name_plural': 'Field matcher Policies'},
- ),
- migrations.AlterModelOptions(
- name='passwordpolicypolicy',
- options={'verbose_name': 'Password Policy Policy', 'verbose_name_plural': 'Password Policy Policies'},
- ),
- migrations.AlterModelOptions(
- name='webhookpolicy',
- options={'verbose_name': 'Webhook Policy', 'verbose_name_plural': 'Webhook Policies'},
- ),
- ]
diff --git a/passbook/core/migrations/0003_auto_20190216_1004.py b/passbook/core/migrations/0003_auto_20190216_1004.py
deleted file mode 100644
index 88363038d..000000000
--- a/passbook/core/migrations/0003_auto_20190216_1004.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-16 10:04
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0002_auto_20190216_1002'),
- ]
-
- operations = [
- migrations.RenameModel(
- old_name='PasswordPolicyPolicy',
- new_name='PasswordPolicy',
- ),
- ]
diff --git a/passbook/core/migrations/0004_auto_20190216_1013.py b/passbook/core/migrations/0004_auto_20190216_1013.py
deleted file mode 100644
index 29b8ce4f7..000000000
--- a/passbook/core/migrations/0004_auto_20190216_1013.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-16 10:13
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0003_auto_20190216_1004'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='passwordpolicy',
- options={'verbose_name': 'Password Policy', 'verbose_name_plural': 'Password Policies'},
- ),
- ]
diff --git a/passbook/core/migrations/0005_auto_20190221_1201.py b/passbook/core/migrations/0005_auto_20190221_1201.py
deleted file mode 100644
index db97c81b0..000000000
--- a/passbook/core/migrations/0005_auto_20190221_1201.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-21 12:01
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0004_auto_20190216_1013'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='policy',
- name='created',
- field=models.DateTimeField(auto_now_add=True),
- ),
- migrations.AlterField(
- model_name='policymodel',
- name='created',
- field=models.DateTimeField(auto_now_add=True),
- ),
- migrations.AlterField(
- model_name='usersourceconnection',
- name='created',
- field=models.DateTimeField(auto_now_add=True),
- ),
- ]
diff --git a/passbook/core/migrations/0006_factor_arguments.py b/passbook/core/migrations/0006_factor_arguments.py
deleted file mode 100644
index 356d25935..000000000
--- a/passbook/core/migrations/0006_factor_arguments.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-21 12:32
-
-import django.contrib.postgres.fields.jsonb
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0005_auto_20190221_1201'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='factor',
- name='arguments',
- field=django.contrib.postgres.fields.jsonb.JSONField(default=dict),
- ),
- ]
diff --git a/passbook/core/migrations/0007_auto_20190221_1233.py b/passbook/core/migrations/0007_auto_20190221_1233.py
deleted file mode 100644
index 377c31dd3..000000000
--- a/passbook/core/migrations/0007_auto_20190221_1233.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-21 12:33
-
-import django.contrib.postgres.fields.jsonb
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0006_factor_arguments'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='factor',
- name='arguments',
- field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict),
- ),
- ]
diff --git a/passbook/core/migrations/0008_auto_20190221_1516.py b/passbook/core/migrations/0008_auto_20190221_1516.py
deleted file mode 100644
index 4c7e00ded..000000000
--- a/passbook/core/migrations/0008_auto_20190221_1516.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-21 15:16
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0007_auto_20190221_1233'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='fieldmatcherpolicy',
- name='match_action',
- field=models.CharField(choices=[('startswith', 'Starts with'), ('endswith', 'Ends with'), ('contains', 'Contains'), ('regexp', 'Regexp'), ('exact', 'Exact')], max_length=50),
- ),
- ]
diff --git a/passbook/core/migrations/0009_auto_20190224_0950.py b/passbook/core/migrations/0009_auto_20190224_0950.py
deleted file mode 100644
index 6e0e39e7f..000000000
--- a/passbook/core/migrations/0009_auto_20190224_0950.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-24 09:50
-
-import django.contrib.postgres.fields
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0008_auto_20190221_1516'),
- ]
-
- operations = [
- migrations.CreateModel(
- 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')),
- ],
- options={
- 'abstract': False,
- },
- bases=('passbook_core.factor',),
- ),
- migrations.CreateModel(
- name='PasswordFactor',
- 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')),
- ('backends', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)),
- ],
- options={
- 'abstract': False,
- },
- bases=('passbook_core.factor',),
- ),
- migrations.RemoveField(
- model_name='factor',
- name='arguments',
- ),
- migrations.RemoveField(
- model_name='factor',
- name='type',
- ),
- ]
diff --git a/passbook/core/migrations/0010_auto_20190224_1016.py b/passbook/core/migrations/0010_auto_20190224_1016.py
deleted file mode 100644
index b0da3563b..000000000
--- a/passbook/core/migrations/0010_auto_20190224_1016.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-24 10:16
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0009_auto_20190224_0950'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='dummyfactor',
- options={'verbose_name': 'Dummy Factor', 'verbose_name_plural': 'Dummy Factors'},
- ),
- migrations.AlterModelOptions(
- name='passwordfactor',
- options={'verbose_name': 'Password Factor', 'verbose_name_plural': 'Password Factors'},
- ),
- ]
diff --git a/passbook/core/migrations/0011_auto_20190225_1438.py b/passbook/core/migrations/0011_auto_20190225_1438.py
deleted file mode 100644
index 690c6f4e7..000000000
--- a/passbook/core/migrations/0011_auto_20190225_1438.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-25 14:38
-
-import django.utils.timezone
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0010_auto_20190224_1016'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='passwordfactor',
- name='password_policies',
- field=models.ManyToManyField(blank=True, to='passbook_core.Policy'),
- ),
- migrations.AddField(
- model_name='user',
- name='password_change_date',
- field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
- preserve_default=False,
- ),
- ]
diff --git a/passbook/core/migrations/0012_nonce.py b/passbook/core/migrations/0012_nonce.py
deleted file mode 100644
index 335ffb3db..000000000
--- a/passbook/core/migrations/0012_nonce.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-25 19:12
-
-import uuid
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-import passbook.core.models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0011_auto_20190225_1438'),
- ]
-
- operations = [
- migrations.CreateModel(
- 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)),
- ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- options={
- 'verbose_name': 'Nonce',
- 'verbose_name_plural': 'Nonces',
- },
- ),
- ]
diff --git a/passbook/core/migrations/0013_invitation_needs_confirmation.py b/passbook/core/migrations/0013_invitation_needs_confirmation.py
deleted file mode 100644
index 4b09597e1..000000000
--- a/passbook/core/migrations/0013_invitation_needs_confirmation.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-25 19:57
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0012_nonce'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='invitation',
- name='needs_confirmation',
- field=models.BooleanField(default=True),
- ),
- ]
diff --git a/passbook/core/migrations/0015_passwordpolicy_error_message.py b/passbook/core/migrations/0015_passwordpolicy_error_message.py
deleted file mode 100644
index 7181f95f5..000000000
--- a/passbook/core/migrations/0015_passwordpolicy_error_message.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-26 14:28
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0014_auto_20190226_0850'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='passwordpolicy',
- name='error_message',
- field=models.TextField(default=''),
- preserve_default=False,
- ),
- ]
diff --git a/passbook/core/migrations/0016_auto_20190227_1355.py b/passbook/core/migrations/0016_auto_20190227_1355.py
deleted file mode 100644
index 8a6a801b9..000000000
--- a/passbook/core/migrations/0016_auto_20190227_1355.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-27 13:55
-
-from django.db import migrations, models
-
-
-def migrate_names(apps, schema_editor):
- """migrate first_name and last_name to name"""
- User = apps.get_model("passbook_core", "User")
- for user in User.objects.all():
- user.name = '%s %s' % (user.first_name, user.last_name)
- user.save()
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0015_passwordpolicy_error_message'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='user',
- name='name',
- field=models.TextField(default=''),
- preserve_default=False,
- ),
- migrations.RunPython(migrate_names),
- migrations.AlterField(
- model_name='user',
- name='name',
- field=models.TextField(),
- preserve_default=False,
- ),
- migrations.AlterField(
- model_name='fieldmatcherpolicy',
- name='user_field',
- field=models.TextField(choices=[('username', 'Username'), ('name', 'Name'), ('email', 'E-Mail'), ('is_staff', 'Is staff'), ('is_active', 'Is active'), ('data_joined', 'Date joined')]),
- ),
- ]
diff --git a/passbook/core/migrations/0017_propertymapping.py b/passbook/core/migrations/0017_propertymapping.py
deleted file mode 100644
index c53c910c1..000000000
--- a/passbook/core/migrations/0017_propertymapping.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-08 10:40
-
-import uuid
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0016_auto_20190227_1355'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='PropertyMapping',
- fields=[
- ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
- ('name', models.TextField()),
- ],
- options={
- 'verbose_name': 'Property Mapping',
- 'verbose_name_plural': 'Property Mappings',
- },
- ),
- ]
diff --git a/passbook/core/migrations/0018_provider_property_mappings.py b/passbook/core/migrations/0018_provider_property_mappings.py
deleted file mode 100644
index a845d1c81..000000000
--- a/passbook/core/migrations/0018_provider_property_mappings.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-08 10:50
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0017_propertymapping'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='provider',
- name='property_mappings',
- field=models.ManyToManyField(blank=True, default=None, to='passbook_core.PropertyMapping'),
- ),
- ]
diff --git a/passbook/core/migrations/0019_auto_20190310_1615.py b/passbook/core/migrations/0019_auto_20190310_1615.py
deleted file mode 100644
index 8b59ed40e..000000000
--- a/passbook/core/migrations/0019_auto_20190310_1615.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-10 16:15
-
-import django.contrib.postgres.fields.hstore
-from django.contrib.postgres.operations import HStoreExtension
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0018_provider_property_mappings'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='group',
- name='extra_data',
- ),
- HStoreExtension(),
- migrations.AddField(
- model_name='group',
- name='tags',
- field=django.contrib.postgres.fields.hstore.HStoreField(default=dict),
- ),
- ]
diff --git a/passbook/core/migrations/0021_policy_timeout.py b/passbook/core/migrations/0021_policy_timeout.py
deleted file mode 100644
index acf278b69..000000000
--- a/passbook/core/migrations/0021_policy_timeout.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-21 12:03
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0020_groupmembershippolicy'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='policy',
- name='timeout',
- field=models.IntegerField(default=30),
- ),
- ]
diff --git a/passbook/core/migrations/0022_nonce_expiring.py b/passbook/core/migrations/0022_nonce_expiring.py
deleted file mode 100644
index 01862a037..000000000
--- a/passbook/core/migrations/0022_nonce_expiring.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1.7 on 2019-04-04 19:42
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0021_policy_timeout'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='nonce',
- name='expiring',
- field=models.BooleanField(default=True),
- ),
- ]
diff --git a/passbook/core/migrations/0023_remove_user_applications.py b/passbook/core/migrations/0023_remove_user_applications.py
deleted file mode 100644
index daaaefcd2..000000000
--- a/passbook/core/migrations/0023_remove_user_applications.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.2 on 2019-04-13 15:51
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0022_nonce_expiring'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='user',
- name='applications',
- ),
- ]
diff --git a/passbook/core/models.py b/passbook/core/models.py
index 7b8cbe0f2..82bdbf9e3 100644
--- a/passbook/core/models.py
+++ b/passbook/core/models.py
@@ -1,12 +1,11 @@
"""passbook core models"""
-import re
from datetime import timedelta
from random import SystemRandom
from time import sleep
from uuid import uuid4
from django.contrib.auth.models import AbstractUser
-from django.contrib.postgres.fields import ArrayField, HStoreField
+from django.contrib.postgres.fields import JSONField
from django.db import models
from django.urls import reverse_lazy
from django.utils.timezone import now
@@ -16,8 +15,8 @@ from structlog import get_logger
from passbook.core.signals import password_changed
from passbook.lib.models import CreatedUpdatedModel, UUIDModel
-from passbook.policy.exceptions import PolicyException
-from passbook.policy.struct import PolicyRequest, PolicyResult
+from passbook.policies.exceptions import PolicyException
+from passbook.policies.struct import PolicyRequest, PolicyResult
LOGGER = get_logger()
@@ -32,10 +31,10 @@ class Group(UUIDModel):
name = models.CharField(_('name'), max_length=80)
parent = models.ForeignKey('Group', blank=True, null=True,
on_delete=models.SET_NULL, related_name='children')
- tags = HStoreField(default=dict)
+ tags = JSONField(default=dict, blank=True)
def __str__(self):
- return "Group %s" % self.name
+ return f"Group {self.name}"
class Meta:
@@ -94,48 +93,8 @@ class Factor(PolicyModel):
return False
def __str__(self):
- return "Factor %s" % self.slug
+ return f"Factor {self.slug}"
-class PasswordFactor(Factor):
- """Password-based Django-backend Authentication Factor"""
-
- backends = ArrayField(models.TextField())
- password_policies = models.ManyToManyField('Policy', blank=True)
-
- type = 'passbook.core.auth.factors.password.PasswordFactor'
- form = 'passbook.core.forms.factors.PasswordFactorForm'
-
- def has_user_settings(self):
- return _('Change Password'), 'pficon-key', 'passbook_core:user-change-password'
-
- def password_passes(self, user: User) -> bool:
- """Return true if user's password passes, otherwise False or raise Exception"""
- for policy in self.policies.all():
- if not policy.passes(user):
- return False
- return True
-
- def __str__(self):
- return "Password Factor %s" % self.slug
-
- class Meta:
-
- verbose_name = _('Password Factor')
- verbose_name_plural = _('Password Factors')
-
-class DummyFactor(Factor):
- """Dummy factor, mostly used to debug"""
-
- type = 'passbook.core.auth.factors.dummy.DummyFactor'
- form = 'passbook.core.forms.factors.DummyFactorForm'
-
- def __str__(self):
- return "Dummy Factor %s" % self.slug
-
- class Meta:
-
- verbose_name = _('Dummy Factor')
- verbose_name_plural = _('Dummy Factors')
class Application(PolicyModel):
"""Every Application which uses passbook for authentication/identification/authorization
@@ -161,6 +120,7 @@ class Application(PolicyModel):
def __str__(self):
return self.name
+
class Source(PolicyModel):
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
@@ -196,6 +156,7 @@ class Source(PolicyModel):
def __str__(self):
return self.name
+
class UserSourceConnection(CreatedUpdatedModel):
"""Connection between User and Source."""
@@ -206,6 +167,7 @@ class UserSourceConnection(CreatedUpdatedModel):
unique_together = (('user', 'source'),)
+
class Policy(UUIDModel, CreatedUpdatedModel):
"""Policies which specify if a user is authorized to use an Application. Can be overridden by
other types to add other fields, more logic, etc."""
@@ -228,148 +190,12 @@ class Policy(UUIDModel, CreatedUpdatedModel):
def __str__(self):
if self.name:
return self.name
- return "%s action %s" % (self.name, self.action)
+ return f"{self.name} action {self.action}"
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if user instance passes this policy"""
raise PolicyException()
-class FieldMatcherPolicy(Policy):
- """Policy which checks if a field of the User model matches/doesn't match a
- certain pattern"""
-
- MATCH_STARTSWITH = 'startswith'
- MATCH_ENDSWITH = 'endswith'
- MATCH_CONTAINS = 'contains'
- MATCH_REGEXP = 'regexp'
- MATCH_EXACT = 'exact'
-
- MATCHES = (
- (MATCH_STARTSWITH, _('Starts with')),
- (MATCH_ENDSWITH, _('Ends with')),
- (MATCH_CONTAINS, _('Contains')),
- (MATCH_REGEXP, _('Regexp')),
- (MATCH_EXACT, _('Exact')),
- )
-
- USER_FIELDS = (
- ('username', _('Username'),),
- ('name', _('Name'),),
- ('email', _('E-Mail'),),
- ('is_staff', _('Is staff'),),
- ('is_active', _('Is active'),),
- ('data_joined', _('Date joined'),),
- )
-
- user_field = models.TextField(choices=USER_FIELDS)
- match_action = models.CharField(max_length=50, choices=MATCHES)
- value = models.TextField()
-
- form = 'passbook.core.forms.policies.FieldMatcherPolicyForm'
-
- def __str__(self):
- description = "%s, user.%s %s '%s'" % (self.name, self.user_field,
- self.match_action, self.value)
- if self.name:
- description = "%s: %s" % (self.name, description)
- return description
-
- def passes(self, request: PolicyRequest) -> PolicyResult:
- """Check if user instance passes this role"""
- if not hasattr(request.user, self.user_field):
- raise ValueError("Field does not exist")
- user_field_value = getattr(request.user, self.user_field, None)
- LOGGER.debug("Checking field", value=user_field_value,
- action=self.match_action, should_be=self.value)
- passes = False
- if self.match_action == FieldMatcherPolicy.MATCH_STARTSWITH:
- passes = user_field_value.startswith(self.value)
- if self.match_action == FieldMatcherPolicy.MATCH_ENDSWITH:
- passes = user_field_value.endswith(self.value)
- if self.match_action == FieldMatcherPolicy.MATCH_CONTAINS:
- passes = self.value in user_field_value
- if self.match_action == FieldMatcherPolicy.MATCH_REGEXP:
- pattern = re.compile(self.value)
- passes = bool(pattern.match(user_field_value))
- if self.match_action == FieldMatcherPolicy.MATCH_EXACT:
- passes = user_field_value == self.value
- return PolicyResult(passes)
-
- class Meta:
-
- verbose_name = _('Field matcher Policy')
- verbose_name_plural = _('Field matcher Policies')
-
-class PasswordPolicy(Policy):
- """Policy to make sure passwords have certain properties"""
-
- amount_uppercase = models.IntegerField(default=0)
- amount_lowercase = models.IntegerField(default=0)
- amount_symbols = models.IntegerField(default=0)
- length_min = models.IntegerField(default=0)
- symbol_charset = models.TextField(default=r"!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ ")
- error_message = models.TextField()
-
- form = 'passbook.core.forms.policies.PasswordPolicyForm'
-
- def passes(self, request: PolicyRequest) -> PolicyResult:
- # Only check if password is being set
- if not hasattr(request.user, '__password__'):
- return PolicyResult(True)
- password = getattr(request.user, '__password__')
-
- filter_regex = r''
- if self.amount_lowercase > 0:
- filter_regex += r'[a-z]{%d,}' % self.amount_lowercase
- if self.amount_uppercase > 0:
- filter_regex += r'[A-Z]{%d,}' % self.amount_uppercase
- if self.amount_symbols > 0:
- filter_regex += r'[%s]{%d,}' % (self.symbol_charset, self.amount_symbols)
- result = bool(re.compile(filter_regex).match(password))
- if not result:
- return PolicyResult(result, self.error_message)
- return PolicyResult(result)
-
- class Meta:
-
- verbose_name = _('Password Policy')
- verbose_name_plural = _('Password Policies')
-
-
-class WebhookPolicy(Policy):
- """Policy that asks webhook"""
-
- METHOD_GET = 'GET'
- METHOD_POST = 'POST'
- METHOD_PATCH = 'PATCH'
- METHOD_DELETE = 'DELETE'
- METHOD_PUT = 'PUT'
-
- METHODS = (
- (METHOD_GET, METHOD_GET),
- (METHOD_POST, METHOD_POST),
- (METHOD_PATCH, METHOD_PATCH),
- (METHOD_DELETE, METHOD_DELETE),
- (METHOD_PUT, METHOD_PUT),
- )
-
- url = models.URLField()
- method = models.CharField(max_length=10, choices=METHODS)
- json_body = models.TextField()
- json_headers = models.TextField()
- result_jsonpath = models.TextField()
- result_json_value = models.TextField()
-
- form = 'passbook.core.forms.policies.WebhookPolicyForm'
-
- def passes(self, request: PolicyRequest) -> PolicyResult:
- """Call webhook asynchronously and report back"""
- raise NotImplementedError()
-
- class Meta:
-
- verbose_name = _('Webhook Policy')
- verbose_name_plural = _('Webhook Policies')
class DebugPolicy(Policy):
"""Policy used for debugging the PolicyEngine. Returns a fixed result,
@@ -393,36 +219,6 @@ class DebugPolicy(Policy):
verbose_name = _('Debug Policy')
verbose_name_plural = _('Debug Policies')
-class GroupMembershipPolicy(Policy):
- """Policy to check if the user is member in a certain group"""
-
- group = models.ForeignKey('Group', on_delete=models.CASCADE)
-
- form = 'passbook.core.forms.policies.GroupMembershipPolicyForm'
-
- def passes(self, request: PolicyRequest) -> PolicyResult:
- return PolicyResult(self.group.user_set.filter(pk=request.user.pk).exists())
-
- class Meta:
-
- verbose_name = _('Group Membership Policy')
- verbose_name_plural = _('Group Membership Policies')
-
-class SSOLoginPolicy(Policy):
- """Policy that applies to users that have authenticated themselves through SSO"""
-
- form = 'passbook.core.forms.policies.SSOLoginPolicyForm'
-
- def passes(self, request: PolicyRequest) -> PolicyResult:
- """Check if user instance passes this policy"""
- from passbook.core.auth.view import AuthenticationView
- is_sso_login = request.user.session.get(AuthenticationView.SESSION_IS_SSO_LOGIN, False)
- return PolicyResult(is_sso_login)
-
- class Meta:
-
- verbose_name = _('SSO Login Policy')
- verbose_name_plural = _('SSO Login Policies')
class Invitation(UUIDModel):
"""Single-use invitation link"""
@@ -436,10 +232,10 @@ class Invitation(UUIDModel):
@property
def link(self):
"""Get link to use invitation"""
- return reverse_lazy('passbook_core:auth-sign-up') + '?invitation=%s' % self.uuid
+ return reverse_lazy('passbook_core:auth-sign-up') + f'?invitation={self.uuid.hex}'
def __str__(self):
- return "Invitation %s created by %s" % (self.uuid, self.created_by)
+ return f"Invitation {self.uuid.hex} created by {self.created_by}"
class Meta:
@@ -454,7 +250,7 @@ class Nonce(UUIDModel):
expiring = models.BooleanField(default=True)
def __str__(self):
- return "Nonce %s (expires=%s)" % (self.uuid.hex, self.expires)
+ return f"Nonce f{self.uuid.hex} (expires={self.expires})"
class Meta:
@@ -470,7 +266,7 @@ class PropertyMapping(UUIDModel):
objects = InheritanceManager()
def __str__(self):
- return "Property Mapping %s" % self.name
+ return f"Property Mapping {self.name}"
class Meta:
diff --git a/passbook/core/settings.py b/passbook/core/settings.py
new file mode 100644
index 000000000..3cd27d42f
--- /dev/null
+++ b/passbook/core/settings.py
@@ -0,0 +1,5 @@
+"""core settings"""
+
+PASSBOOK_CORE_FACTORS = [
+
+]
diff --git a/passbook/core/signals.py b/passbook/core/signals.py
index 66842f1e8..741af6094 100644
--- a/passbook/core/signals.py
+++ b/passbook/core/signals.py
@@ -5,8 +5,6 @@ from django.db.models.signals import post_save
from django.dispatch import receiver
from structlog import get_logger
-from passbook.core.exceptions import PasswordPolicyInvalid
-
LOGGER = get_logger()
user_signed_up = Signal(providing_args=['request', 'user'])
@@ -14,24 +12,9 @@ invitation_created = Signal(providing_args=['request', 'invitation'])
invitation_used = Signal(providing_args=['request', 'invitation', 'user'])
password_changed = Signal(providing_args=['user', 'password'])
-@receiver(password_changed)
-# pylint: disable=unused-argument
-def password_policy_checker(sender, password, **kwargs):
- """Run password through all password policies which are applied to the user"""
- from passbook.core.models import PasswordFactor
- from passbook.policy.engine import PolicyEngine
- setattr(sender, '__password__', password)
- _all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
- for factor in _all_factors:
- policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses())
- policy_engine.for_user(sender).build()
- passing, messages = policy_engine.result
- if not passing:
- raise PasswordPolicyInvalid(*messages)
-
@receiver(post_save)
# pylint: disable=unused-argument
-def invalidate_policy_cache(sender, instance, **kwargs):
+def invalidate_policy_cache(sender, instance, **_):
"""Invalidate Policy cache when policy is updated"""
from passbook.core.models import Policy
if isinstance(instance, Policy):
diff --git a/passbook/core/templatetags/passbook_user_settings.py b/passbook/core/templatetags/passbook_user_settings.py
index 05bfc0ae6..f560d03d3 100644
--- a/passbook/core/templatetags/passbook_user_settings.py
+++ b/passbook/core/templatetags/passbook_user_settings.py
@@ -1,14 +1,15 @@
"""passbook user settings template tags"""
from django import template
+from django.template.context import RequestContext
from passbook.core.models import Factor, Source
-from passbook.policy.engine import PolicyEngine
+from passbook.policies.engine import PolicyEngine
register = template.Library()
@register.simple_tag(takes_context=True)
-def user_factors(context):
+def user_factors(context: RequestContext):
"""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()
@@ -22,7 +23,7 @@ def user_factors(context):
return matching_factors
@register.simple_tag(takes_context=True)
-def user_sources(context):
+def user_sources(context: RequestContext):
"""Return a list of all sources which are enabled for the user"""
user = context.get('request').user
_all_sources = Source.objects.filter(enabled=True).select_subclasses()
diff --git a/passbook/core/urls.py b/passbook/core/urls.py
index 560f292be..7857a41e0 100644
--- a/passbook/core/urls.py
+++ b/passbook/core/urls.py
@@ -2,8 +2,8 @@
from django.urls import path
from structlog import get_logger
-from passbook.core.auth import view
from passbook.core.views import authentication, overview, user
+from passbook.factors import view
LOGGER = get_logger()
diff --git a/passbook/core/views/access.py b/passbook/core/views/access.py
index 9d1155460..fa9bad3e0 100644
--- a/passbook/core/views/access.py
+++ b/passbook/core/views/access.py
@@ -7,7 +7,7 @@ from django.utils.translation import gettext as _
from structlog import get_logger
from passbook.core.models import Application, Provider, User
-from passbook.policy.engine import PolicyEngine
+from passbook.policies.engine import PolicyEngine
LOGGER = get_logger()
diff --git a/passbook/core/views/authentication.py b/passbook/core/views/authentication.py
index 0f957d797..c2767467e 100644
--- a/passbook/core/views/authentication.py
+++ b/passbook/core/views/authentication.py
@@ -12,12 +12,12 @@ from django.views import View
from django.views.generic import FormView
from structlog import get_logger
-from passbook.core.auth.view import AuthenticationView, _redirect_with_qs
-from passbook.core.exceptions import PasswordPolicyInvalid
from passbook.core.forms.authentication import LoginForm, SignUpForm
from passbook.core.models import Invitation, Nonce, Source, User
from passbook.core.signals import invitation_used, user_signed_up
from passbook.core.tasks import send_email
+from passbook.factors.password.exceptions import PasswordPolicyInvalid
+from passbook.factors.view import AuthenticationView, _redirect_with_qs
from passbook.lib.config import CONFIG
LOGGER = get_logger()
diff --git a/passbook/core/views/overview.py b/passbook/core/views/overview.py
index ad788b607..efe37e17c 100644
--- a/passbook/core/views/overview.py
+++ b/passbook/core/views/overview.py
@@ -4,7 +4,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
from passbook.core.models import Application
-from passbook.policy.engine import PolicyEngine
+from passbook.policies.engine import PolicyEngine
class OverviewView(LoginRequiredMixin, TemplateView):
diff --git a/passbook/core/views/user.py b/passbook/core/views/user.py
index 6de4ad003..4f90a1421 100644
--- a/passbook/core/views/user.py
+++ b/passbook/core/views/user.py
@@ -9,8 +9,8 @@ from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import DeleteView, FormView, UpdateView
-from passbook.core.exceptions import PasswordPolicyInvalid
from passbook.core.forms.users import PasswordChangeForm, UserDetailForm
+from passbook.factors.password.exceptions import PasswordPolicyInvalid
from passbook.lib.config import CONFIG
diff --git a/passbook/app_gw/__init__.py b/passbook/factors/__init__.py
similarity index 100%
rename from passbook/app_gw/__init__.py
rename to passbook/factors/__init__.py
diff --git a/passbook/core/auth/factor.py b/passbook/factors/base.py
similarity index 66%
rename from passbook/core/auth/factor.py
rename to passbook/factors/base.py
index 98b26232b..f4754bf53 100644
--- a/passbook/core/auth/factor.py
+++ b/passbook/factors/base.py
@@ -1,21 +1,25 @@
"""passbook multi-factor authentication engine"""
+from django.forms import ModelForm
+from django.http import HttpRequest
from django.utils.translation import gettext as _
from django.views.generic import TemplateView
+from passbook.core.models import User
+from passbook.factors.view import AuthenticationView
from passbook.lib.config import CONFIG
class AuthenticationFactor(TemplateView):
"""Abstract Authentication factor, inherits TemplateView but can be combined with FormView"""
- form = None
- required = True
- authenticator = None
- pending_user = None
- request = None
+ form: ModelForm = None
+ required: bool = True
+ authenticator: AuthenticationView = None
+ pending_user: User = None
+ request: HttpRequest = None
template_name = 'login/form_with_user.html'
- def __init__(self, authenticator):
+ def __init__(self, authenticator: AuthenticationView):
self.authenticator = authenticator
def get_context_data(self, **kwargs):
diff --git a/passbook/app_gw/migrations/__init__.py b/passbook/factors/captcha/__init__.py
similarity index 100%
rename from passbook/app_gw/migrations/__init__.py
rename to passbook/factors/captcha/__init__.py
diff --git a/passbook/factors/captcha/apps.py b/passbook/factors/captcha/apps.py
new file mode 100644
index 000000000..5b4e0c722
--- /dev/null
+++ b/passbook/factors/captcha/apps.py
@@ -0,0 +1,10 @@
+"""passbook captcha app"""
+from django.apps import AppConfig
+
+
+class PassbookFactorCaptchaConfig(AppConfig):
+ """passbook captcha app"""
+
+ name = 'passbook.factors.captcha'
+ label = 'passbook_factors_captcha'
+ verbose_name = 'passbook Factors.Captcha'
diff --git a/passbook/captcha_factor/factor.py b/passbook/factors/captcha/factor.py
similarity index 84%
rename from passbook/captcha_factor/factor.py
rename to passbook/factors/captcha/factor.py
index 9ce564cff..9017bc674 100644
--- a/passbook/captcha_factor/factor.py
+++ b/passbook/factors/captcha/factor.py
@@ -2,8 +2,8 @@
from django.views.generic import FormView
-from passbook.captcha_factor.forms import CaptchaForm
-from passbook.core.auth.factor import AuthenticationFactor
+from passbook.factors.base import AuthenticationFactor
+from passbook.factors.captcha.forms import CaptchaForm
class CaptchaFactor(FormView, AuthenticationFactor):
@@ -16,6 +16,7 @@ class CaptchaFactor(FormView, AuthenticationFactor):
def get_form(self, form_class=None):
form = CaptchaForm(**self.get_form_kwargs())
+ # TODO: uuuhm
form.fields['captcha'].public_key = '6Lfi1w8TAAAAAELH-YiWp0OFItmMzvjGmw2xkvUN'
form.fields['captcha'].private_key = '6Lfi1w8TAAAAAMQI3f86tGMvd1QkcqqVQyBWI23D'
form.fields['captcha'].widget.attrs["data-sitekey"] = form.fields['captcha'].public_key
diff --git a/passbook/captcha_factor/forms.py b/passbook/factors/captcha/forms.py
similarity index 84%
rename from passbook/captcha_factor/forms.py
rename to passbook/factors/captcha/forms.py
index ed48968ff..687b44a46 100644
--- a/passbook/captcha_factor/forms.py
+++ b/passbook/factors/captcha/forms.py
@@ -2,8 +2,8 @@
from captcha.fields import ReCaptchaField
from django import forms
-from passbook.captcha_factor.models import CaptchaFactor
-from passbook.core.forms.factors import GENERAL_FIELDS
+from passbook.factors.captcha.models import CaptchaFactor
+from passbook.factors.forms import GENERAL_FIELDS
class CaptchaForm(forms.Form):
diff --git a/passbook/captcha_factor/migrations/0001_initial.py b/passbook/factors/captcha/migrations/0001_initial.py
similarity index 88%
rename from passbook/captcha_factor/migrations/0001_initial.py
rename to passbook/factors/captcha/migrations/0001_initial.py
index 97d2be499..e08a8efdd 100644
--- a/passbook/captcha_factor/migrations/0001_initial.py
+++ b/passbook/factors/captcha/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-02-24 21:35
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
@@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('passbook_core', '0010_auto_20190224_1016'),
+ ('passbook_core', '0001_initial'),
]
operations = [
diff --git a/passbook/captcha_factor/__init__.py b/passbook/factors/captcha/migrations/__init__.py
similarity index 100%
rename from passbook/captcha_factor/__init__.py
rename to passbook/factors/captcha/migrations/__init__.py
diff --git a/passbook/captcha_factor/models.py b/passbook/factors/captcha/models.py
similarity index 71%
rename from passbook/captcha_factor/models.py
rename to passbook/factors/captcha/models.py
index cb0319ac2..9b45bbb57 100644
--- a/passbook/captcha_factor/models.py
+++ b/passbook/factors/captcha/models.py
@@ -11,11 +11,11 @@ class CaptchaFactor(Factor):
public_key = models.TextField()
private_key = models.TextField()
- type = 'passbook.captcha_factor.factor.CaptchaFactor'
- form = 'passbook.captcha_factor.forms.CaptchaFactorForm'
+ type = 'passbook.factors.captcha.factor.CaptchaFactor'
+ form = 'passbook.factors.captcha.forms.CaptchaFactorForm'
def __str__(self):
- return "Captcha Factor %s" % self.slug
+ return f"Captcha Factor {self.slug}"
class Meta:
diff --git a/passbook/captcha_factor/settings.py b/passbook/factors/captcha/settings.py
similarity index 61%
rename from passbook/captcha_factor/settings.py
rename to passbook/factors/captcha/settings.py
index 0b888f511..01e98b632 100644
--- a/passbook/captcha_factor/settings.py
+++ b/passbook/factors/captcha/settings.py
@@ -1,6 +1,8 @@
-"""passbook captcha_facot settings"""
+"""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'
+
NOCAPTCHA = True
INSTALLED_APPS = [
'captcha'
diff --git a/passbook/captcha_factor/migrations/__init__.py b/passbook/factors/dummy/__init__.py
similarity index 100%
rename from passbook/captcha_factor/migrations/__init__.py
rename to passbook/factors/dummy/__init__.py
diff --git a/passbook/factors/dummy/admin.py b/passbook/factors/dummy/admin.py
new file mode 100644
index 000000000..0c3f336fc
--- /dev/null
+++ b/passbook/factors/dummy/admin.py
@@ -0,0 +1,5 @@
+"""dummy factor admin"""
+
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_factors_dummy')
diff --git a/passbook/factors/dummy/apps.py b/passbook/factors/dummy/apps.py
new file mode 100644
index 000000000..2a5eb82c3
--- /dev/null
+++ b/passbook/factors/dummy/apps.py
@@ -0,0 +1,11 @@
+"""passbook dummy factor config"""
+
+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'
diff --git a/passbook/core/auth/factors/dummy.py b/passbook/factors/dummy/factor.py
similarity index 62%
rename from passbook/core/auth/factors/dummy.py
rename to passbook/factors/dummy/factor.py
index ebfd29ed6..63fce66c1 100644
--- a/passbook/core/auth/factors/dummy.py
+++ b/passbook/factors/dummy/factor.py
@@ -1,14 +1,12 @@
"""passbook multi-factor authentication engine"""
-from structlog import get_logger
+from django.http import HttpRequest
-from passbook.core.auth.factor import AuthenticationFactor
-
-LOGGER = get_logger()
+from passbook.factors.base import AuthenticationFactor
class DummyFactor(AuthenticationFactor):
"""Dummy factor for testing with multiple factors"""
- def post(self, request):
+ def post(self, request: HttpRequest):
"""Just redirect to next factor"""
return self.authenticator.user_ok()
diff --git a/passbook/factors/dummy/forms.py b/passbook/factors/dummy/forms.py
new file mode 100644
index 000000000..cf5a9145e
--- /dev/null
+++ b/passbook/factors/dummy/forms.py
@@ -0,0 +1,21 @@
+"""passbook administration forms"""
+from django import forms
+from django.contrib.admin.widgets import FilteredSelectMultiple
+from django.utils.translation import gettext as _
+
+from passbook.factors.dummy.models import DummyFactor
+from passbook.factors.forms import GENERAL_FIELDS
+
+
+class DummyFactorForm(forms.ModelForm):
+ """Form to create/edit Dummy Factor"""
+
+ class Meta:
+
+ model = DummyFactor
+ fields = GENERAL_FIELDS
+ widgets = {
+ 'name': forms.TextInput(),
+ 'order': forms.NumberInput(),
+ 'policies': FilteredSelectMultiple(_('policies'), False)
+ }
diff --git a/passbook/factors/dummy/migrations/0001_initial.py b/passbook/factors/dummy/migrations/0001_initial.py
new file mode 100644
index 000000000..59abab393
--- /dev/null
+++ b/passbook/factors/dummy/migrations/0001_initial.py
@@ -0,0 +1,27 @@
+# Generated by Django 2.2.6 on 2019-10-07 14:07
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('passbook_core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ 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')),
+ ],
+ options={
+ 'verbose_name': 'Dummy Factor',
+ 'verbose_name_plural': 'Dummy Factors',
+ },
+ bases=('passbook_core.factor',),
+ ),
+ ]
diff --git a/passbook/core/auth/__init__.py b/passbook/factors/dummy/migrations/__init__.py
similarity index 100%
rename from passbook/core/auth/__init__.py
rename to passbook/factors/dummy/migrations/__init__.py
diff --git a/passbook/factors/dummy/models.py b/passbook/factors/dummy/models.py
new file mode 100644
index 000000000..bbf4c6b36
--- /dev/null
+++ b/passbook/factors/dummy/models.py
@@ -0,0 +1,19 @@
+"""dummy factor models"""
+from django.utils.translation import gettext as _
+
+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'
+
+ def __str__(self):
+ return f"Dummy Factor {self.slug}"
+
+ class Meta:
+
+ verbose_name = _('Dummy Factor')
+ verbose_name_plural = _('Dummy Factors')
diff --git a/passbook/factors/forms.py b/passbook/factors/forms.py
new file mode 100644
index 000000000..1f65394ac
--- /dev/null
+++ b/passbook/factors/forms.py
@@ -0,0 +1,3 @@
+"""factor forms"""
+
+GENERAL_FIELDS = ['name', 'slug', 'order', 'policies', 'enabled']
diff --git a/passbook/core/auth/factors/__init__.py b/passbook/factors/otp/__init__.py
similarity index 100%
rename from passbook/core/auth/factors/__init__.py
rename to passbook/factors/otp/__init__.py
diff --git a/passbook/factors/otp/apps.py b/passbook/factors/otp/apps.py
new file mode 100644
index 000000000..1fc1be8e6
--- /dev/null
+++ b/passbook/factors/otp/apps.py
@@ -0,0 +1,12 @@
+"""passbook OTP AppConfig"""
+
+from django.apps.config import AppConfig
+
+
+class PassbookFactorOTPConfig(AppConfig):
+ """passbook OTP AppConfig"""
+
+ name = 'passbook.factors.otp'
+ label = 'passbook_factors_otp'
+ verbose_name = 'passbook Factors.OTP'
+ mountpoint = 'user/otp/'
diff --git a/passbook/otp/factors.py b/passbook/factors/otp/factors.py
similarity index 92%
rename from passbook/otp/factors.py
rename to passbook/factors/otp/factors.py
index bab0cb796..9cae91761 100644
--- a/passbook/otp/factors.py
+++ b/passbook/factors/otp/factors.py
@@ -5,9 +5,9 @@ from django.views.generic import FormView
from django_otp import match_token, user_has_device
from structlog import get_logger
-from passbook.core.auth.factor import AuthenticationFactor
-from passbook.otp.forms import OTPVerifyForm
-from passbook.otp.views import OTP_SETTING_UP_KEY, EnableView
+from passbook.factors.base import AuthenticationFactor
+from passbook.factors.otp.forms import OTPVerifyForm
+from passbook.factors.otp.views import OTP_SETTING_UP_KEY, EnableView
LOGGER = get_logger()
diff --git a/passbook/otp/forms.py b/passbook/factors/otp/forms.py
similarity index 89%
rename from passbook/otp/forms.py
rename to passbook/factors/otp/forms.py
index bfe41e0bc..16a37908a 100644
--- a/passbook/otp/forms.py
+++ b/passbook/factors/otp/forms.py
@@ -5,9 +5,10 @@ from django.contrib.admin.widgets import FilteredSelectMultiple
from django.core.validators import RegexValidator
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
+from django_otp.models import Device
-from passbook.core.forms.factors import GENERAL_FIELDS
-from passbook.otp.models import OTPFactor
+from passbook.factors.forms import GENERAL_FIELDS
+from passbook.factors.otp.models import OTPFactor
OTP_CODE_VALIDATOR = RegexValidator(r'^[0-9a-z]{6,8}$',
_('Only alpha-numeric characters are allowed.'))
@@ -17,7 +18,7 @@ class PictureWidget(forms.widgets.Widget):
"""Widget to render value as img-tag"""
def render(self, name, value, attrs=None, renderer=None):
- return mark_safe("" % value) # nosec
+ return mark_safe(f'') # nosec
class OTPVerifyForm(forms.Form):
@@ -33,13 +34,14 @@ class OTPVerifyForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# This is a little helper so the field is focused by default
+ # TODO: Tell browser to not suggest any values
self.fields['code'].widget.attrs.update({'autofocus': 'autofocus'})
class OTPSetupForm(forms.Form):
"""OTP Setup form"""
title = _('Set up OTP')
- device = None
+ device: Device = None
qr_code = forms.CharField(widget=PictureWidget, disabled=True, required=False,
label=_('Scan this Code with your OTP App.'))
code = forms.CharField(label=_('Code'), validators=[OTP_CODE_VALIDATOR],
diff --git a/passbook/otp/migrations/0001_initial.py b/passbook/factors/otp/migrations/0001_initial.py
similarity index 88%
rename from passbook/otp/migrations/0001_initial.py
rename to passbook/factors/otp/migrations/0001_initial.py
index 9e7483c43..0acab2c9d 100644
--- a/passbook/otp/migrations/0001_initial.py
+++ b/passbook/factors/otp/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-02-25 09:42
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
@@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('passbook_core', '0010_auto_20190224_1016'),
+ ('passbook_core', '0001_initial'),
]
operations = [
diff --git a/passbook/core/management/__init__.py b/passbook/factors/otp/migrations/__init__.py
similarity index 100%
rename from passbook/core/management/__init__.py
rename to passbook/factors/otp/migrations/__init__.py
diff --git a/passbook/otp/models.py b/passbook/factors/otp/models.py
similarity index 80%
rename from passbook/otp/models.py
rename to passbook/factors/otp/models.py
index 15d2249e7..74e2a54a6 100644
--- a/passbook/otp/models.py
+++ b/passbook/factors/otp/models.py
@@ -12,14 +12,14 @@ class OTPFactor(Factor):
enforced = models.BooleanField(default=False, help_text=('Enforce enabled OTP for Users '
'this factor applies to.'))
- type = 'passbook.otp.factors.OTPFactor'
- form = 'passbook.otp.forms.OTPFactorForm'
+ type = 'passbook.factors.otp.factors.OTPFactor'
+ form = 'passbook.factors.otp.forms.OTPFactorForm'
def has_user_settings(self):
return _('OTP'), 'pficon-locked', 'passbook_otp:otp-user-settings'
def __str__(self):
- return "OTP Factor %s" % self.slug
+ return f"OTP Factor {self.slug}"
class Meta:
diff --git a/passbook/otp/settings.py b/passbook/factors/otp/settings.py
similarity index 100%
rename from passbook/otp/settings.py
rename to passbook/factors/otp/settings.py
diff --git a/passbook/otp/templates/otp/factor.html b/passbook/factors/otp/templates/otp/factor.html
similarity index 100%
rename from passbook/otp/templates/otp/factor.html
rename to passbook/factors/otp/templates/otp/factor.html
diff --git a/passbook/otp/templates/otp/setup.html b/passbook/factors/otp/templates/otp/setup.html
similarity index 100%
rename from passbook/otp/templates/otp/setup.html
rename to passbook/factors/otp/templates/otp/setup.html
diff --git a/passbook/otp/templates/otp/user_settings.html b/passbook/factors/otp/templates/otp/user_settings.html
similarity index 100%
rename from passbook/otp/templates/otp/user_settings.html
rename to passbook/factors/otp/templates/otp/user_settings.html
diff --git a/passbook/otp/urls.py b/passbook/factors/otp/urls.py
similarity index 89%
rename from passbook/otp/urls.py
rename to passbook/factors/otp/urls.py
index 96574c117..6da05b400 100644
--- a/passbook/otp/urls.py
+++ b/passbook/factors/otp/urls.py
@@ -2,7 +2,7 @@
from django.urls import path
-from passbook.otp import views
+from passbook.factors.otp import views
urlpatterns = [
path('', views.UserSettingsView.as_view(), name='otp-user-settings'),
diff --git a/passbook/otp/utils.py b/passbook/factors/otp/utils.py
similarity index 100%
rename from passbook/otp/utils.py
rename to passbook/factors/otp/utils.py
diff --git a/passbook/otp/views.py b/passbook/factors/otp/views.py
similarity index 98%
rename from passbook/otp/views.py
rename to passbook/factors/otp/views.py
index 3c01bd851..84f3b7a0a 100644
--- a/passbook/otp/views.py
+++ b/passbook/factors/otp/views.py
@@ -16,10 +16,10 @@ from qrcode import make
from qrcode.image.svg import SvgPathImage
from structlog import get_logger
+from passbook.factors.otp.forms import OTPSetupForm
+from passbook.factors.otp.utils import otpauth_url
from passbook.lib.boilerplate import NeverCacheMixin
from passbook.lib.config import CONFIG
-from passbook.otp.forms import OTPSetupForm
-from passbook.otp.utils import otpauth_url
OTP_SESSION_KEY = 'passbook_otp_key'
OTP_SETTING_UP_KEY = 'passbook_otp_setup'
diff --git a/passbook/core/management/commands/__init__.py b/passbook/factors/password/__init__.py
similarity index 100%
rename from passbook/core/management/commands/__init__.py
rename to passbook/factors/password/__init__.py
diff --git a/passbook/factors/password/admin.py b/passbook/factors/password/admin.py
new file mode 100644
index 000000000..1ff90534f
--- /dev/null
+++ b/passbook/factors/password/admin.py
@@ -0,0 +1,5 @@
+"""password factor admin"""
+
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_factors_password')
diff --git a/passbook/factors/password/apps.py b/passbook/factors/password/apps.py
new file mode 100644
index 000000000..2ab933c0d
--- /dev/null
+++ b/passbook/factors/password/apps.py
@@ -0,0 +1,15 @@
+"""passbook core app config"""
+from importlib import import_module
+
+from django.apps import AppConfig
+
+
+class PassbookFactorPasswordConfig(AppConfig):
+ """passbook password factor config"""
+
+ name = 'passbook.factors.password'
+ label = 'passbook_factors_password'
+ verbose_name = 'passbook Factors.Password'
+
+ def ready(self):
+ import_module('passbook.factors.password.signals')
diff --git a/passbook/core/exceptions.py b/passbook/factors/password/exceptions.py
similarity index 83%
rename from passbook/core/exceptions.py
rename to passbook/factors/password/exceptions.py
index b5f5b5277..b04d2668f 100644
--- a/passbook/core/exceptions.py
+++ b/passbook/factors/password/exceptions.py
@@ -1,4 +1,4 @@
-"""passbook core exceptions"""
+"""passbook password policy exceptions"""
class PasswordPolicyInvalid(Exception):
"""Exception raised when a Password Policy fails"""
diff --git a/passbook/core/auth/factors/password.py b/passbook/factors/password/factor.py
similarity index 97%
rename from passbook/core/auth/factors/password.py
rename to passbook/factors/password/factor.py
index 948687c64..afccdc242 100644
--- a/passbook/core/auth/factors/password.py
+++ b/passbook/factors/password/factor.py
@@ -11,11 +11,11 @@ from django.utils.translation import gettext as _
from django.views.generic import FormView
from structlog import get_logger
-from passbook.core.auth.factor import AuthenticationFactor
-from passbook.core.auth.view import AuthenticationView
from passbook.core.forms.authentication import PasswordFactorForm
from passbook.core.models import Nonce
from passbook.core.tasks import send_email
+from passbook.factors.base import AuthenticationFactor
+from passbook.factors.view import AuthenticationView
from passbook.lib.config import CONFIG
from passbook.lib.utils.reflection import path_to_class
@@ -24,6 +24,7 @@ LOGGER = get_logger()
def authenticate(request, backends, **credentials):
"""If the given credentials are valid, return a User object.
+
Customized version of django's authenticate, which accepts a list of backends"""
for backend_path in backends:
backend = path_to_class(backend_path)()
diff --git a/passbook/core/forms/factors.py b/passbook/factors/password/forms.py
similarity index 71%
rename from passbook/core/forms/factors.py
rename to passbook/factors/password/forms.py
index b304eb966..c790c4d43 100644
--- a/passbook/core/forms/factors.py
+++ b/passbook/factors/password/forms.py
@@ -4,10 +4,10 @@ from django.conf import settings
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext as _
-from passbook.core.models import DummyFactor, PasswordFactor
+from passbook.factors.forms import GENERAL_FIELDS
+from passbook.factors.password.models import PasswordFactor
from passbook.lib.utils.reflection import path_to_class
-GENERAL_FIELDS = ['name', 'slug', 'order', 'policies', 'enabled']
def get_authentication_backends():
"""Return all available authentication backends as tuple set"""
@@ -15,6 +15,7 @@ def get_authentication_backends():
klass = path_to_class(backend)
yield backend, getattr(klass(), 'name', '%s (%s)' % (klass.__name__, klass.__module__))
+
class PasswordFactorForm(forms.ModelForm):
"""Form to create/edit Password Factors"""
@@ -30,16 +31,3 @@ class PasswordFactorForm(forms.ModelForm):
choices=get_authentication_backends()),
'password_policies': FilteredSelectMultiple(_('password policies'), False),
}
-
-class DummyFactorForm(forms.ModelForm):
- """Form to create/edit Dummy Factor"""
-
- class Meta:
-
- model = DummyFactor
- fields = GENERAL_FIELDS
- widgets = {
- 'name': forms.TextInput(),
- 'order': forms.NumberInput(),
- 'policies': FilteredSelectMultiple(_('policies'), False)
- }
diff --git a/passbook/factors/password/migrations/0001_initial.py b/passbook/factors/password/migrations/0001_initial.py
new file mode 100644
index 000000000..68cfaa8a0
--- /dev/null
+++ b/passbook/factors/password/migrations/0001_initial.py
@@ -0,0 +1,30 @@
+# Generated by Django 2.2.6 on 2019-10-07 14:07
+
+import django.contrib.postgres.fields
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('passbook_core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PasswordFactor',
+ 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')),
+ ('backends', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)),
+ ('password_policies', models.ManyToManyField(blank=True, to='passbook_core.Policy')),
+ ],
+ options={
+ 'verbose_name': 'Password Factor',
+ 'verbose_name_plural': 'Password Factors',
+ },
+ bases=('passbook_core.factor',),
+ ),
+ ]
diff --git a/passbook/core/migrations/0014_auto_20190226_0850.py b/passbook/factors/password/migrations/0002_auto_20191007_1411.py
similarity index 73%
rename from passbook/core/migrations/0014_auto_20190226_0850.py
rename to passbook/factors/password/migrations/0002_auto_20191007_1411.py
index 1b9ed8c68..b7e15c08c 100644
--- a/passbook/core/migrations/0014_auto_20190226_0850.py
+++ b/passbook/factors/password/migrations/0002_auto_20191007_1411.py
@@ -1,11 +1,11 @@
-# Generated by Django 2.1.7 on 2019-02-26 08:50
+# Generated by Django 2.2.6 on 2019-10-07 14:11
from django.db import migrations
def create_initial_factor(apps, schema_editor):
"""Create initial PasswordFactor if none exists"""
- PasswordFactor = apps.get_model("passbook_core", "PasswordFactor")
+ PasswordFactor = apps.get_model("passbook_factors_password", "PasswordFactor")
if not PasswordFactor.objects.exists():
PasswordFactor.objects.create(
name='password',
@@ -17,7 +17,7 @@ def create_initial_factor(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
- ('passbook_core', '0013_invitation_needs_confirmation'),
+ ('passbook_factors_password', '0001_initial'),
]
operations = [
diff --git a/passbook/hibp_policy/__init__.py b/passbook/factors/password/migrations/__init__.py
similarity index 100%
rename from passbook/hibp_policy/__init__.py
rename to passbook/factors/password/migrations/__init__.py
diff --git a/passbook/factors/password/models.py b/passbook/factors/password/models.py
new file mode 100644
index 000000000..b255e87e6
--- /dev/null
+++ b/passbook/factors/password/models.py
@@ -0,0 +1,34 @@
+"""password factor models"""
+from django.contrib.postgres.fields import ArrayField
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+
+from passbook.core.models import Factor, Policy, User
+
+
+class PasswordFactor(Factor):
+ """Password-based Django-backend Authentication Factor"""
+
+ backends = ArrayField(models.TextField())
+ password_policies = models.ManyToManyField(Policy, blank=True)
+
+ type = 'passbook.factors.password.factor.PasswordFactor'
+ form = 'passbook.factors.password.forms.PasswordFactorForm'
+
+ def has_user_settings(self):
+ return _('Change Password'), 'pficon-key', 'passbook_core:user-change-password'
+
+ def password_passes(self, user: User) -> bool:
+ """Return true if user's password passes, otherwise False or raise Exception"""
+ for policy in self.policies.all():
+ if not policy.passes(user):
+ return False
+ return True
+
+ def __str__(self):
+ return "Password Factor %s" % self.slug
+
+ class Meta:
+
+ verbose_name = _('Password Factor')
+ verbose_name_plural = _('Password Factors')
diff --git a/passbook/factors/password/signals.py b/passbook/factors/password/signals.py
new file mode 100644
index 000000000..cf7cea028
--- /dev/null
+++ b/passbook/factors/password/signals.py
@@ -0,0 +1,20 @@
+"""passbook password factor signals"""
+from django.dispatch import receiver
+
+from passbook.core.signals import password_changed
+from passbook.factors.password.exceptions import PasswordPolicyInvalid
+
+
+@receiver(password_changed)
+def password_policy_checker(sender, password, **_):
+ """Run password through all password policies which are applied to the user"""
+ from passbook.factors.password.models import PasswordFactor
+ from passbook.policies.engine import PolicyEngine
+ setattr(sender, '__password__', password)
+ _all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
+ for factor in _all_factors:
+ policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses())
+ policy_engine.for_user(sender).build()
+ passing, messages = policy_engine.result
+ if not passing:
+ raise PasswordPolicyInvalid(*messages)
diff --git a/passbook/hibp_policy/migrations/__init__.py b/passbook/factors/tests/__init__.py
similarity index 100%
rename from passbook/hibp_policy/migrations/__init__.py
rename to passbook/factors/tests/__init__.py
diff --git a/passbook/core/tests/test_auth_view.py b/passbook/factors/tests/test_auth_view.py
similarity index 96%
rename from passbook/core/tests/test_auth_view.py
rename to passbook/factors/tests/test_auth_view.py
index 430936029..df447d930 100644
--- a/passbook/core/tests/test_auth_view.py
+++ b/passbook/factors/tests/test_auth_view.py
@@ -7,8 +7,10 @@ from django.contrib.sessions.middleware import SessionMiddleware
from django.test import RequestFactory, TestCase
from django.urls import reverse
-from passbook.core.auth.view import AuthenticationView
-from passbook.core.models import DummyFactor, PasswordFactor, User
+from passbook.core.models import User
+from passbook.factors.dummy.models import DummyFactor
+from passbook.factors.password.models import PasswordFactor
+from passbook.factors.view import AuthenticationView
class TestFactorAuthentication(TestCase):
diff --git a/passbook/core/auth/view.py b/passbook/factors/view.py
similarity index 96%
rename from passbook/core/auth/view.py
rename to passbook/factors/view.py
index 5516380ae..d53d38502 100644
--- a/passbook/core/auth/view.py
+++ b/passbook/factors/view.py
@@ -12,7 +12,7 @@ from passbook.core.models import Factor, User
from passbook.core.views.utils import PermissionDeniedView
from passbook.lib.utils.reflection import class_to_path, path_to_class
from passbook.lib.utils.urls import is_url_absolute
-from passbook.policy.engine import PolicyEngine
+from passbook.policies.engine import PolicyEngine
LOGGER = get_logger()
@@ -23,6 +23,10 @@ def _redirect_with_qs(view, get_query_set=None):
target += '?' + urlencode({key: value for key, value in get_query_set.items()})
return redirect(target)
+
+# Argument used to redirect user after login
+NEXT_ARG_NAME = 'next'
+
class AuthenticationView(UserPassesTestMixin, View):
"""Wizard-like Multi-factor authenticator"""
@@ -45,8 +49,8 @@ class AuthenticationView(UserPassesTestMixin, View):
def handle_no_permission(self):
# Function from UserPassesTestMixin
- if 'next' in self.request.GET:
- return redirect(self.request.GET.get('next'))
+ if NEXT_ARG_NAME in self.request.GET:
+ return redirect(self.request.GET.get(NEXT_ARG_NAME))
if self.request.user.is_authenticated:
return _redirect_with_qs('passbook_core:overview', self.request.GET)
return _redirect_with_qs('passbook_core:auth-login', self.request.GET)
@@ -147,7 +151,7 @@ class AuthenticationView(UserPassesTestMixin, View):
LOGGER.debug("Logged in", user=self.pending_user)
# Cleanup
self.cleanup()
- next_param = self.request.GET.get('next', None)
+ next_param = self.request.GET.get(NEXT_ARG_NAME, None)
if next_param and not is_url_absolute(next_param):
return redirect(next_param)
return _redirect_with_qs('passbook_core:overview')
diff --git a/passbook/hibp_policy/apps.py b/passbook/hibp_policy/apps.py
deleted file mode 100644
index 7e71824bc..000000000
--- a/passbook/hibp_policy/apps.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""Passbook hibp app config"""
-
-from django.apps import AppConfig
-
-
-class PassbookHIBPConfig(AppConfig):
- """Passbook hibp app config"""
-
- name = 'passbook.hibp_policy'
- label = 'passbook_hibp_policy'
- verbose_name = 'passbook HaveIBeenPwned Policy'
diff --git a/passbook/hibp_policy/migrations/0002_auto_20190225_1912.py b/passbook/hibp_policy/migrations/0002_auto_20190225_1912.py
deleted file mode 100644
index 6d7f9eabf..000000000
--- a/passbook/hibp_policy/migrations/0002_auto_20190225_1912.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-25 19:12
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_hibp_policy', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='haveibeenpwendpolicy',
- options={'verbose_name': 'have i been pwned Policy', 'verbose_name_plural': 'have i been pwned Policies'},
- ),
- ]
diff --git a/passbook/hibp_policy/migrations/0003_auto_20190227_1505.py b/passbook/hibp_policy/migrations/0003_auto_20190227_1505.py
deleted file mode 100644
index f37bc6a90..000000000
--- a/passbook/hibp_policy/migrations/0003_auto_20190227_1505.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-27 15:05
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_hibp_policy', '0002_auto_20190225_1912'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='haveibeenpwendpolicy',
- options={'verbose_name': 'Have I Been Pwned Policy', 'verbose_name_plural': 'Have I Been Pwned Policies'},
- ),
- ]
diff --git a/passbook/ldap/apps.py b/passbook/ldap/apps.py
deleted file mode 100644
index 909e61360..000000000
--- a/passbook/ldap/apps.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""Passbook ldap app config"""
-
-from django.apps import AppConfig
-
-
-class PassbookLdapConfig(AppConfig):
- """Passbook ldap app config"""
-
- name = 'passbook.ldap'
- label = 'passbook_ldap'
- verbose_name = 'passbook LDAP'
diff --git a/passbook/ldap/migrations/0002_ldapgroupmembershippolicy.py b/passbook/ldap/migrations/0002_ldapgroupmembershippolicy.py
deleted file mode 100644
index a7f2bed7e..000000000
--- a/passbook/ldap/migrations/0002_ldapgroupmembershippolicy.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-10 18:38
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0020_groupmembershippolicy'),
- ('passbook_ldap', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='LDAPGroupMembershipPolicy',
- 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')),
- ('dn', models.TextField()),
- ('source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='passbook_ldap.LDAPSource')),
- ],
- options={
- 'verbose_name': 'LDAP Group Membership Policy',
- 'verbose_name_plural': 'LDAP Group Membership Policys',
- },
- bases=('passbook_core.policy',),
- ),
- ]
diff --git a/passbook/lib/default.yml b/passbook/lib/default.yml
index c7f09fb21..8c2bedaf6 100644
--- a/passbook/lib/default.yml
+++ b/passbook/lib/default.yml
@@ -61,22 +61,6 @@ ldap:
username: "%(sAMAccountName)s"
email: "%(mail)s"
name: "%(displayName)"
-oauth_client:
- # List of python packages with sources types to load.
- types:
- - passbook.oauth_client.source_types.discord
- - passbook.oauth_client.source_types.facebook
- - passbook.oauth_client.source_types.github
- - passbook.oauth_client.source_types.google
- - passbook.oauth_client.source_types.reddit
- - passbook.oauth_client.source_types.supervisr
- - passbook.oauth_client.source_types.twitter
- - passbook.oauth_client.source_types.azure_ad
-saml_idp:
- # List of python packages with provider types to load.
- types:
- - passbook.saml_idp.processors.generic
- - passbook.saml_idp.processors.salesforce
app_gw:
listen: 0.0.0.0
port: 8000
diff --git a/passbook/oauth_client/settings.py b/passbook/oauth_client/settings.py
deleted file mode 100644
index 82f40f720..000000000
--- a/passbook/oauth_client/settings.py
+++ /dev/null
@@ -1,7 +0,0 @@
-"""
-Oauth2 Client Settings
-"""
-
-AUTHENTICATION_BACKENDS = [
- 'passbook.oauth_client.backends.AuthorizedServiceBackend',
-]
diff --git a/passbook/oidc_provider/migrations/0002_auto_20190709_1416.py b/passbook/oidc_provider/migrations/0002_auto_20190709_1416.py
deleted file mode 100644
index 6bc8bc225..000000000
--- a/passbook/oidc_provider/migrations/0002_auto_20190709_1416.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.2.3 on 2019-07-09 14:16
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_oidc_provider', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='openidprovider',
- options={'verbose_name': 'OpenID Provider', 'verbose_name_plural': 'OpenID Providers'},
- ),
- ]
diff --git a/passbook/oidc_provider/settings.py b/passbook/oidc_provider/settings.py
deleted file mode 100644
index dfde0f058..000000000
--- a/passbook/oidc_provider/settings.py
+++ /dev/null
@@ -1,7 +0,0 @@
-"""passbook OIDC Provider"""
-
-INSTALLED_APPS = [
- 'oidc_provider',
-]
-
-OIDC_AFTER_USERLOGIN_HOOK = "passbook.oidc_provider.lib.check_permissions"
diff --git a/passbook/otp/apps.py b/passbook/otp/apps.py
deleted file mode 100644
index b7e7e1fa8..000000000
--- a/passbook/otp/apps.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""passbook OTP AppConfig"""
-
-from django.apps.config import AppConfig
-
-
-class PassbookOTPConfig(AppConfig):
- """passbook OTP AppConfig"""
-
- name = 'passbook.otp'
- label = 'passbook_otp'
- verbose_name = 'passbook OTP'
- mountpoint = 'user/otp/'
diff --git a/passbook/password_expiry_policy/admin.py b/passbook/password_expiry_policy/admin.py
deleted file mode 100644
index 67192491d..000000000
--- a/passbook/password_expiry_policy/admin.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""Passbook password_expiry_policy Admin"""
-
-from passbook.lib.admin import admin_autoregister
-
-admin_autoregister('passbook_password_expiry_policy')
diff --git a/passbook/password_expiry_policy/apps.py b/passbook/password_expiry_policy/apps.py
deleted file mode 100644
index c335659f9..000000000
--- a/passbook/password_expiry_policy/apps.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""Passbook password_expiry_policy app config"""
-
-from django.apps import AppConfig
-
-
-class PassbookPasswordExpiryPolicyConfig(AppConfig):
- """Passbook password_expiry_policy app config"""
-
- name = 'passbook.password_expiry_policy'
- label = 'passbook_password_expiry_policy'
- verbose_name = 'passbook Password Expiry Policy'
diff --git a/passbook/ldap/__init__.py b/passbook/policies/__init__.py
similarity index 100%
rename from passbook/ldap/__init__.py
rename to passbook/policies/__init__.py
diff --git a/passbook/policy/engine.py b/passbook/policies/engine.py
similarity index 93%
rename from passbook/policy/engine.py
rename to passbook/policies/engine.py
index 2c418ffb6..bc661bea1 100644
--- a/passbook/policy/engine.py
+++ b/passbook/policies/engine.py
@@ -8,14 +8,11 @@ from django.http import HttpRequest
from structlog import get_logger
from passbook.core.models import Policy, User
-from passbook.policy.process import PolicyProcess
-from passbook.policy.struct import PolicyRequest, PolicyResult
+from passbook.policies.process import PolicyProcess, cache_key
+from passbook.policies.struct import PolicyRequest, PolicyResult
LOGGER = get_logger()
-def _cache_key(policy, user):
- return f"policy_{policy.pk}#{user.pk}"
-
class PolicyProcessInfo:
"""Dataclass to hold all information and communication channels to a process"""
@@ -69,7 +66,7 @@ class PolicyEngine:
request = PolicyRequest(self.__user)
request.http_request = self.__request
for policy in self._select_subclasses():
- cached_policy = cache.get(_cache_key(policy, self.__user), None)
+ cached_policy = cache.get(cache_key(policy, self.__user), None)
if cached_policy:
LOGGER.debug("Taking result from cache", policy=policy)
cached_policies.append(cached_policy)
diff --git a/passbook/policy/exceptions.py b/passbook/policies/exceptions.py
similarity index 100%
rename from passbook/policy/exceptions.py
rename to passbook/policies/exceptions.py
diff --git a/passbook/ldap/migrations/__init__.py b/passbook/policies/expiry/__init__.py
similarity index 100%
rename from passbook/ldap/migrations/__init__.py
rename to passbook/policies/expiry/__init__.py
diff --git a/passbook/policies/expiry/admin.py b/passbook/policies/expiry/admin.py
new file mode 100644
index 000000000..2623d7989
--- /dev/null
+++ b/passbook/policies/expiry/admin.py
@@ -0,0 +1,5 @@
+"""Passbook passbook expiry policy Admin"""
+
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_policies_expiry')
diff --git a/passbook/policies/expiry/apps.py b/passbook/policies/expiry/apps.py
new file mode 100644
index 000000000..b218da9d9
--- /dev/null
+++ b/passbook/policies/expiry/apps.py
@@ -0,0 +1,11 @@
+"""Passbook policy_expiry app config"""
+
+from django.apps import AppConfig
+
+
+class PassbookPolicyExpiryConfig(AppConfig):
+ """Passbook policy_expiry app config"""
+
+ name = 'passbook.policies.expiry'
+ label = 'passbook_policies_expiry'
+ verbose_name = 'passbook Policies.Expiry'
diff --git a/passbook/password_expiry_policy/forms.py b/passbook/policies/expiry/forms.py
similarity index 91%
rename from passbook/password_expiry_policy/forms.py
rename to passbook/policies/expiry/forms.py
index be496cd0b..0ead5c233 100644
--- a/passbook/password_expiry_policy/forms.py
+++ b/passbook/policies/expiry/forms.py
@@ -5,7 +5,7 @@ from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext as _
from passbook.core.forms.policies import GENERAL_FIELDS
-from passbook.password_expiry_policy.models import PasswordExpiryPolicy
+from passbook.policies.expiry.models import PasswordExpiryPolicy
class PasswordExpiryPolicyForm(forms.ModelForm):
diff --git a/passbook/password_expiry_policy/migrations/0001_initial.py b/passbook/policies/expiry/migrations/0001_initial.py
similarity index 89%
rename from passbook/password_expiry_policy/migrations/0001_initial.py
rename to passbook/policies/expiry/migrations/0001_initial.py
index 50f28fe06..38809d9cd 100644
--- a/passbook/password_expiry_policy/migrations/0001_initial.py
+++ b/passbook/policies/expiry/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-03-03 13:46
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
@@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('passbook_core', '0016_auto_20190227_1355'),
+ ('passbook_core', '0001_initial'),
]
operations = [
diff --git a/passbook/oauth_client/__init__.py b/passbook/policies/expiry/migrations/__init__.py
similarity index 100%
rename from passbook/oauth_client/__init__.py
rename to passbook/policies/expiry/migrations/__init__.py
diff --git a/passbook/password_expiry_policy/models.py b/passbook/policies/expiry/models.py
similarity index 92%
rename from passbook/password_expiry_policy/models.py
rename to passbook/policies/expiry/models.py
index ed5d7fa9d..b7d3271ea 100644
--- a/passbook/password_expiry_policy/models.py
+++ b/passbook/policies/expiry/models.py
@@ -7,7 +7,7 @@ from django.utils.translation import gettext as _
from structlog import get_logger
from passbook.core.models import Policy
-from passbook.policy.struct import PolicyRequest, PolicyResult
+from passbook.policies.struct import PolicyRequest, PolicyResult
LOGGER = get_logger()
@@ -19,7 +19,7 @@ class PasswordExpiryPolicy(Policy):
deny_only = models.BooleanField(default=False)
days = models.IntegerField()
- form = 'passbook.password_expiry_policy.forms.PasswordExpiryPolicyForm'
+ form = 'passbook.policies.expiry.forms.PasswordExpiryPolicyForm'
def passes(self, request: PolicyRequest) -> PolicyResult:
"""If password change date is more than x days in the past, call set_unusable_password
diff --git a/passbook/policies/forms.py b/passbook/policies/forms.py
new file mode 100644
index 000000000..19bf89588
--- /dev/null
+++ b/passbook/policies/forms.py
@@ -0,0 +1,3 @@
+"""General fields"""
+
+GENERAL_FIELDS = ['name', 'action', 'negate', 'order', 'timeout']
diff --git a/passbook/oauth_client/migrations/__init__.py b/passbook/policies/group/__init__.py
similarity index 100%
rename from passbook/oauth_client/migrations/__init__.py
rename to passbook/policies/group/__init__.py
diff --git a/passbook/policies/group/admin.py b/passbook/policies/group/admin.py
new file mode 100644
index 000000000..7206844ec
--- /dev/null
+++ b/passbook/policies/group/admin.py
@@ -0,0 +1,4 @@
+"""autodiscover admin"""
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_policies_group')
diff --git a/passbook/policies/group/apps.py b/passbook/policies/group/apps.py
new file mode 100644
index 000000000..f27b34954
--- /dev/null
+++ b/passbook/policies/group/apps.py
@@ -0,0 +1,11 @@
+"""passbook Group policy app config"""
+
+from django.apps import AppConfig
+
+
+class PassbookPoliciesGroupConfig(AppConfig):
+ """passbook Group policy app config"""
+
+ name = 'passbook.policies.group'
+ label = 'passbook_policies_group'
+ verbose_name = 'passbook Policies.Group'
diff --git a/passbook/policies/group/forms.py b/passbook/policies/group/forms.py
new file mode 100644
index 000000000..5bb43c0d4
--- /dev/null
+++ b/passbook/policies/group/forms.py
@@ -0,0 +1,19 @@
+"""passbook Policy forms"""
+
+from django import forms
+
+from passbook.policies.forms import GENERAL_FIELDS
+from passbook.policies.group.models import GroupMembershipPolicy
+
+
+class GroupMembershipPolicyForm(forms.ModelForm):
+ """GroupMembershipPolicy Form"""
+
+ class Meta:
+
+ model = GroupMembershipPolicy
+ fields = GENERAL_FIELDS + ['group', ]
+ widgets = {
+ 'name': forms.TextInput(),
+ 'order': forms.NumberInput(),
+ }
diff --git a/passbook/core/migrations/0020_groupmembershippolicy.py b/passbook/policies/group/migrations/0001_initial.py
similarity index 88%
rename from passbook/core/migrations/0020_groupmembershippolicy.py
rename to passbook/policies/group/migrations/0001_initial.py
index f120f4908..a2b255184 100644
--- a/passbook/core/migrations/0020_groupmembershippolicy.py
+++ b/passbook/policies/group/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-03-10 18:25
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
@@ -6,8 +6,10 @@ from django.db import migrations, models
class Migration(migrations.Migration):
+ initial = True
+
dependencies = [
- ('passbook_core', '0019_auto_20190310_1615'),
+ ('passbook_core', '0001_initial'),
]
operations = [
diff --git a/passbook/oauth_client/source_types/__init__.py b/passbook/policies/group/migrations/__init__.py
similarity index 100%
rename from passbook/oauth_client/source_types/__init__.py
rename to passbook/policies/group/migrations/__init__.py
diff --git a/passbook/policies/group/models.py b/passbook/policies/group/models.py
new file mode 100644
index 000000000..149eb2c0d
--- /dev/null
+++ b/passbook/policies/group/models.py
@@ -0,0 +1,22 @@
+"""passbook group models models"""
+from django.db import models
+from django.utils.translation import gettext as _
+
+from passbook.core.models import Group, Policy
+from passbook.policies.struct import PolicyRequest, PolicyResult
+
+
+class GroupMembershipPolicy(Policy):
+ """Policy to check if the user is member in a certain group"""
+
+ group = models.ForeignKey(Group, on_delete=models.CASCADE)
+
+ form = 'passbook.policies.group.forms.GroupMembershipPolicyForm'
+
+ def passes(self, request: PolicyRequest) -> PolicyResult:
+ return PolicyResult(self.group.user_set.filter(pk=request.user.pk).exists())
+
+ class Meta:
+
+ verbose_name = _('Group Membership Policy')
+ verbose_name_plural = _('Group Membership Policies')
diff --git a/passbook/oauth_client/views/__init__.py b/passbook/policies/hibp/__init__.py
similarity index 100%
rename from passbook/oauth_client/views/__init__.py
rename to passbook/policies/hibp/__init__.py
diff --git a/passbook/hibp_policy/admin.py b/passbook/policies/hibp/admin.py
similarity index 63%
rename from passbook/hibp_policy/admin.py
rename to passbook/policies/hibp/admin.py
index b8f5efb9d..a8f568fd5 100644
--- a/passbook/hibp_policy/admin.py
+++ b/passbook/policies/hibp/admin.py
@@ -2,4 +2,4 @@
from passbook.lib.admin import admin_autoregister
-admin_autoregister('passbook_hibp_policy')
+admin_autoregister('passbook_policies_hibp')
diff --git a/passbook/policies/hibp/apps.py b/passbook/policies/hibp/apps.py
new file mode 100644
index 000000000..090b3cbf7
--- /dev/null
+++ b/passbook/policies/hibp/apps.py
@@ -0,0 +1,11 @@
+"""Passbook hibp app config"""
+
+from django.apps import AppConfig
+
+
+class PassbookPolicyHIBPConfig(AppConfig):
+ """Passbook hibp app config"""
+
+ name = 'passbook.policies.hibp'
+ label = 'passbook_policies_hibp'
+ verbose_name = 'passbook Policies.HaveIBeenPwned'
diff --git a/passbook/hibp_policy/forms.py b/passbook/policies/hibp/forms.py
similarity index 90%
rename from passbook/hibp_policy/forms.py
rename to passbook/policies/hibp/forms.py
index 643bf4791..5557097f6 100644
--- a/passbook/hibp_policy/forms.py
+++ b/passbook/policies/hibp/forms.py
@@ -5,7 +5,7 @@ from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext as _
from passbook.core.forms.policies import GENERAL_FIELDS
-from passbook.hibp_policy.models import HaveIBeenPwendPolicy
+from passbook.policies.hibp.models import HaveIBeenPwendPolicy
class HaveIBeenPwnedPolicyForm(forms.ModelForm):
diff --git a/passbook/hibp_policy/migrations/0001_initial.py b/passbook/policies/hibp/migrations/0001_initial.py
similarity index 74%
rename from passbook/hibp_policy/migrations/0001_initial.py
rename to passbook/policies/hibp/migrations/0001_initial.py
index f183157c7..b6069d17b 100644
--- a/passbook/hibp_policy/migrations/0001_initial.py
+++ b/passbook/policies/hibp/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-02-25 15:50
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
@@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('passbook_core', '0011_auto_20190225_1438'),
+ ('passbook_core', '0001_initial'),
]
operations = [
@@ -20,8 +20,8 @@ class Migration(migrations.Migration):
('allowed_count', models.IntegerField(default=0)),
],
options={
- 'verbose_name': 'HaveIBeenPwned Policy',
- 'verbose_name_plural': 'HaveIBeenPwned Policies',
+ 'verbose_name': 'Have I Been Pwned Policy',
+ 'verbose_name_plural': 'Have I Been Pwned Policies',
},
bases=('passbook_core.policy',),
),
diff --git a/passbook/oauth_provider/__init__.py b/passbook/policies/hibp/migrations/__init__.py
similarity index 100%
rename from passbook/oauth_provider/__init__.py
rename to passbook/policies/hibp/migrations/__init__.py
diff --git a/passbook/hibp_policy/models.py b/passbook/policies/hibp/models.py
similarity index 96%
rename from passbook/hibp_policy/models.py
rename to passbook/policies/hibp/models.py
index eb8c3bb06..b737b6b2b 100644
--- a/passbook/hibp_policy/models.py
+++ b/passbook/policies/hibp/models.py
@@ -16,7 +16,7 @@ class HaveIBeenPwendPolicy(Policy):
allowed_count = models.IntegerField(default=0)
- form = 'passbook.hibp_policy.forms.HaveIBeenPwnedPolicyForm'
+ form = 'passbook.policies.hibp.forms.HaveIBeenPwnedPolicyForm'
def passes(self, user: User) -> PolicyResult:
"""Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5
diff --git a/passbook/oauth_provider/migrations/__init__.py b/passbook/policies/matcher/__init__.py
similarity index 100%
rename from passbook/oauth_provider/migrations/__init__.py
rename to passbook/policies/matcher/__init__.py
diff --git a/passbook/policies/matcher/admin.py b/passbook/policies/matcher/admin.py
new file mode 100644
index 000000000..c81b555a6
--- /dev/null
+++ b/passbook/policies/matcher/admin.py
@@ -0,0 +1,4 @@
+"""autodiscover admin"""
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_policies_matcher')
diff --git a/passbook/policies/matcher/apps.py b/passbook/policies/matcher/apps.py
new file mode 100644
index 000000000..8d19ddd19
--- /dev/null
+++ b/passbook/policies/matcher/apps.py
@@ -0,0 +1,11 @@
+"""passbook Matcher policy app config"""
+
+from django.apps import AppConfig
+
+
+class PassbookPoliciesMatcherConfig(AppConfig):
+ """passbook Matcher policy app config"""
+
+ name = 'passbook.policies.matcher'
+ label = 'passbook_policies_matcher'
+ verbose_name = 'passbook Policies.Matcher'
diff --git a/passbook/policies/matcher/forms.py b/passbook/policies/matcher/forms.py
new file mode 100644
index 000000000..13b4e4f36
--- /dev/null
+++ b/passbook/policies/matcher/forms.py
@@ -0,0 +1,19 @@
+"""passbook Policy forms"""
+
+from django import forms
+
+from passbook.policies.forms import GENERAL_FIELDS
+from passbook.policies.matcher.models import FieldMatcherPolicy
+
+
+class FieldMatcherPolicyForm(forms.ModelForm):
+ """FieldMatcherPolicy Form"""
+
+ class Meta:
+
+ model = FieldMatcherPolicy
+ fields = GENERAL_FIELDS + ['user_field', 'match_action', 'value', ]
+ widgets = {
+ 'name': forms.TextInput(),
+ 'value': forms.TextInput(),
+ }
diff --git a/passbook/policies/matcher/migrations/0001_initial.py b/passbook/policies/matcher/migrations/0001_initial.py
new file mode 100644
index 000000000..c1da71ea1
--- /dev/null
+++ b/passbook/policies/matcher/migrations/0001_initial.py
@@ -0,0 +1,30 @@
+# Generated by Django 2.2.6 on 2019-10-07 14:07
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('passbook_core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='FieldMatcherPolicy',
+ 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')),
+ ('user_field', models.TextField(choices=[('username', 'Username'), ('name', 'Name'), ('email', 'E-Mail'), ('is_staff', 'Is staff'), ('is_active', 'Is active'), ('data_joined', 'Date joined')])),
+ ('match_action', models.CharField(choices=[('startswith', 'Starts with'), ('endswith', 'Ends with'), ('contains', 'Contains'), ('regexp', 'Regexp'), ('exact', 'Exact')], max_length=50)),
+ ('value', models.TextField()),
+ ],
+ options={
+ 'verbose_name': 'Field matcher Policy',
+ 'verbose_name_plural': 'Field matcher Policies',
+ },
+ bases=('passbook_core.policy',),
+ ),
+ ]
diff --git a/passbook/oauth_provider/views/__init__.py b/passbook/policies/matcher/migrations/__init__.py
similarity index 100%
rename from passbook/oauth_provider/views/__init__.py
rename to passbook/policies/matcher/migrations/__init__.py
diff --git a/passbook/policies/matcher/models.py b/passbook/policies/matcher/models.py
new file mode 100644
index 000000000..dec2f4b1c
--- /dev/null
+++ b/passbook/policies/matcher/models.py
@@ -0,0 +1,76 @@
+"""user field matcher models"""
+import re
+
+from django.db import models
+from django.utils.translation import gettext as _
+from structlog import get_logger
+
+from passbook.core.models import Policy
+from passbook.policies.struct import PolicyRequest, PolicyResult
+
+LOGGER = get_logger()
+
+class FieldMatcherPolicy(Policy):
+ """Policy which checks if a field of the User model matches/doesn't match a
+ certain pattern"""
+
+ MATCH_STARTSWITH = 'startswith'
+ MATCH_ENDSWITH = 'endswith'
+ MATCH_CONTAINS = 'contains'
+ MATCH_REGEXP = 'regexp'
+ MATCH_EXACT = 'exact'
+
+ MATCHES = (
+ (MATCH_STARTSWITH, _('Starts with')),
+ (MATCH_ENDSWITH, _('Ends with')),
+ (MATCH_CONTAINS, _('Contains')),
+ (MATCH_REGEXP, _('Regexp')),
+ (MATCH_EXACT, _('Exact')),
+ )
+
+ USER_FIELDS = (
+ ('username', _('Username'),),
+ ('name', _('Name'),),
+ ('email', _('E-Mail'),),
+ ('is_staff', _('Is staff'),),
+ ('is_active', _('Is active'),),
+ ('data_joined', _('Date joined'),),
+ )
+
+ user_field = models.TextField(choices=USER_FIELDS)
+ match_action = models.CharField(max_length=50, choices=MATCHES)
+ value = models.TextField()
+
+ form = 'passbook.policies.matcher.forms.FieldMatcherPolicyForm'
+
+ def __str__(self):
+ description = f"{self.name}, user.{self.user_field} {self.match_action} '{self.value}'"
+ if self.name:
+ description = f"{self.name}: {description}"
+ return description
+
+ def passes(self, request: PolicyRequest) -> PolicyResult:
+ """Check if user instance passes this role"""
+ if not hasattr(request.user, self.user_field):
+ raise ValueError("Field does not exist")
+ user_field_value = getattr(request.user, self.user_field, None)
+ LOGGER.debug("Checking field", value=user_field_value,
+ action=self.match_action, should_be=self.value)
+ passes = False
+ if self.match_action == FieldMatcherPolicy.MATCH_STARTSWITH:
+ passes = user_field_value.startswith(self.value)
+ if self.match_action == FieldMatcherPolicy.MATCH_ENDSWITH:
+ passes = user_field_value.endswith(self.value)
+ if self.match_action == FieldMatcherPolicy.MATCH_CONTAINS:
+ passes = self.value in user_field_value
+ if self.match_action == FieldMatcherPolicy.MATCH_REGEXP:
+ pattern = re.compile(self.value)
+ passes = bool(pattern.match(user_field_value))
+ if self.match_action == FieldMatcherPolicy.MATCH_EXACT:
+ passes = user_field_value == self.value
+ return PolicyResult(passes)
+
+ class Meta:
+
+ verbose_name = _('Field matcher Policy')
+ verbose_name_plural = _('Field matcher Policies')
diff --git a/passbook/oidc_provider/__init__.py b/passbook/policies/password/__init__.py
similarity index 100%
rename from passbook/oidc_provider/__init__.py
rename to passbook/policies/password/__init__.py
diff --git a/passbook/policies/password/admin.py b/passbook/policies/password/admin.py
new file mode 100644
index 000000000..919eeae08
--- /dev/null
+++ b/passbook/policies/password/admin.py
@@ -0,0 +1,4 @@
+"""autodiscover admin"""
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_policies_password')
diff --git a/passbook/policies/password/apps.py b/passbook/policies/password/apps.py
new file mode 100644
index 000000000..e8a3abc47
--- /dev/null
+++ b/passbook/policies/password/apps.py
@@ -0,0 +1,11 @@
+"""passbook Password policy app config"""
+
+from django.apps import AppConfig
+
+
+class PassbookPoliciesPasswordConfig(AppConfig):
+ """passbook Password policy app config"""
+
+ name = 'passbook.policies.password'
+ label = 'passbook_policies_password'
+ verbose_name = 'passbook Policies.Password'
diff --git a/passbook/policies/password/forms.py b/passbook/policies/password/forms.py
new file mode 100644
index 000000000..ba505f7b3
--- /dev/null
+++ b/passbook/policies/password/forms.py
@@ -0,0 +1,29 @@
+"""passbook Policy forms"""
+
+from django import forms
+from django.utils.translation import gettext as _
+
+from passbook.policies.forms import GENERAL_FIELDS
+from passbook.policies.password.models import PasswordPolicy
+
+
+class PasswordPolicyForm(forms.ModelForm):
+ """PasswordPolicy Form"""
+
+ class Meta:
+
+ model = PasswordPolicy
+ fields = GENERAL_FIELDS + ['amount_uppercase', 'amount_lowercase',
+ 'amount_symbols', 'length_min', 'symbol_charset',
+ 'error_message']
+ widgets = {
+ 'name': forms.TextInput(),
+ 'symbol_charset': forms.TextInput(),
+ 'error_message': forms.TextInput(),
+ }
+ labels = {
+ 'amount_uppercase': _('Minimum amount of Uppercase Characters'),
+ 'amount_lowercase': _('Minimum amount of Lowercase Characters'),
+ 'amount_symbols': _('Minimum amount of Symbols Characters'),
+ 'length_min': _('Minimum Length'),
+ }
diff --git a/passbook/policies/password/migrations/0001_initial.py b/passbook/policies/password/migrations/0001_initial.py
new file mode 100644
index 000000000..389ed060e
--- /dev/null
+++ b/passbook/policies/password/migrations/0001_initial.py
@@ -0,0 +1,33 @@
+# Generated by Django 2.2.6 on 2019-10-07 14:07
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('passbook_core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PasswordPolicy',
+ 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')),
+ ('amount_uppercase', models.IntegerField(default=0)),
+ ('amount_lowercase', models.IntegerField(default=0)),
+ ('amount_symbols', models.IntegerField(default=0)),
+ ('length_min', models.IntegerField(default=0)),
+ ('symbol_charset', models.TextField(default='!\\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ')),
+ ('error_message', models.TextField()),
+ ],
+ options={
+ 'verbose_name': 'Password Policy',
+ 'verbose_name_plural': 'Password Policies',
+ },
+ bases=('passbook_core.policy',),
+ ),
+ ]
diff --git a/passbook/oidc_provider/migrations/__init__.py b/passbook/policies/password/migrations/__init__.py
similarity index 100%
rename from passbook/oidc_provider/migrations/__init__.py
rename to passbook/policies/password/migrations/__init__.py
diff --git a/passbook/policies/password/models.py b/passbook/policies/password/models.py
new file mode 100644
index 000000000..b7f72be32
--- /dev/null
+++ b/passbook/policies/password/models.py
@@ -0,0 +1,47 @@
+"""user field matcher models"""
+import re
+
+from django.db import models
+from django.utils.translation import gettext as _
+from structlog import get_logger
+
+from passbook.core.models import Policy
+from passbook.policies.struct import PolicyRequest, PolicyResult
+
+LOGGER = get_logger()
+
+
+class PasswordPolicy(Policy):
+ """Policy to make sure passwords have certain properties"""
+
+ amount_uppercase = models.IntegerField(default=0)
+ amount_lowercase = models.IntegerField(default=0)
+ amount_symbols = models.IntegerField(default=0)
+ length_min = models.IntegerField(default=0)
+ symbol_charset = models.TextField(default=r"!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ ")
+ error_message = models.TextField()
+
+ form = 'passbook.policies.password.forms.PasswordPolicyForm'
+
+ def passes(self, request: PolicyRequest) -> PolicyResult:
+ # Only check if password is being set
+ if not hasattr(request.user, '__password__'):
+ return PolicyResult(True)
+ password = getattr(request.user, '__password__')
+
+ filter_regex = r''
+ if self.amount_lowercase > 0:
+ filter_regex += r'[a-z]{%d,}' % self.amount_lowercase
+ if self.amount_uppercase > 0:
+ filter_regex += r'[A-Z]{%d,}' % self.amount_uppercase
+ if self.amount_symbols > 0:
+ filter_regex += r'[%s]{%d,}' % (self.symbol_charset, self.amount_symbols)
+ result = bool(re.compile(filter_regex).match(password))
+ if not result:
+ return PolicyResult(result, self.error_message)
+ return PolicyResult(result)
+
+ class Meta:
+
+ verbose_name = _('Password Policy')
+ verbose_name_plural = _('Password Policies')
diff --git a/passbook/policy/process.py b/passbook/policies/process.py
similarity index 75%
rename from passbook/policy/process.py
rename to passbook/policies/process.py
index 9427e93f2..c12375359 100644
--- a/passbook/policy/process.py
+++ b/passbook/policies/process.py
@@ -2,17 +2,19 @@
from multiprocessing import Process
from multiprocessing.connection import Connection
+from django.core.cache import cache
from structlog import get_logger
from passbook.core.models import Policy
-from passbook.policy.exceptions import PolicyException
-from passbook.policy.struct import PolicyRequest, PolicyResult
+from passbook.policies.exceptions import PolicyException
+from passbook.policies.struct import PolicyRequest, PolicyResult
LOGGER = get_logger()
-def _cache_key(policy, user):
- return "policy_%s#%s" % (policy.uuid, user.pk)
+def cache_key(policy, user):
+ """Generate Cache key for policy"""
+ return f"policy_{policy.pk}#{user.pk}"
class PolicyProcess(Process):
"""Evaluate a single policy within a seprate process"""
@@ -41,7 +43,7 @@ class PolicyProcess(Process):
policy_result = not policy_result
LOGGER.debug("Got result", policy=self.policy, result=policy_result,
process="PolicyProcess")
- # cache_key = _cache_key(self.policy, self.request.user)
- # cache.set(cache_key, (self.policy.action, policy_result, message))
- # LOGGER.debug("Cached entry as %s", cache_key)
+ key = cache_key(self.policy, self.request.user)
+ cache.set(key, policy_result)
+ LOGGER.debug("Cached policy evaluation", key=key)
self.connection.send(policy_result)
diff --git a/passbook/otp/__init__.py b/passbook/policies/reputation/__init__.py
similarity index 100%
rename from passbook/otp/__init__.py
rename to passbook/policies/reputation/__init__.py
diff --git a/passbook/policies/reputation/admin.py b/passbook/policies/reputation/admin.py
new file mode 100644
index 000000000..354368fc4
--- /dev/null
+++ b/passbook/policies/reputation/admin.py
@@ -0,0 +1,5 @@
+"""Passbook reputation Admin"""
+
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_policies_reputation')
diff --git a/passbook/policies/reputation/apps.py b/passbook/policies/reputation/apps.py
new file mode 100644
index 000000000..663b8e307
--- /dev/null
+++ b/passbook/policies/reputation/apps.py
@@ -0,0 +1,15 @@
+"""Passbook reputation_policy app config"""
+from importlib import import_module
+
+from django.apps import AppConfig
+
+
+class PassbookPolicyReputationConfig(AppConfig):
+ """Passbook reputation app config"""
+
+ name = 'passbook.policies.reputation'
+ label = 'passbook_policies_reputation'
+ verbose_name = 'passbook Policies.Reputation'
+
+ def ready(self):
+ import_module('passbook.policies.reputation.signals')
diff --git a/passbook/suspicious_policy/forms.py b/passbook/policies/reputation/forms.py
similarity index 53%
rename from passbook/suspicious_policy/forms.py
rename to passbook/policies/reputation/forms.py
index 45758aafc..53d2e53eb 100644
--- a/passbook/suspicious_policy/forms.py
+++ b/passbook/policies/reputation/forms.py
@@ -1,16 +1,16 @@
-"""passbook suspicious request forms"""
+"""passbook reputation request forms"""
from django import forms
from passbook.core.forms.policies import GENERAL_FIELDS
-from passbook.suspicious_policy.models import SuspiciousRequestPolicy
+from passbook.policies.reputation.models import ReputationPolicy
-class SuspiciousRequestPolicyForm(forms.ModelForm):
- """Form to edit SuspiciousRequestPolicy"""
+class ReputationPolicyForm(forms.ModelForm):
+ """Form to edit ReputationPolicy"""
class Meta:
- model = SuspiciousRequestPolicy
+ model = ReputationPolicy
fields = GENERAL_FIELDS + ['check_ip', 'check_username', 'threshold']
widgets = {
'name': forms.TextInput(),
diff --git a/passbook/suspicious_policy/migrations/0001_initial.py b/passbook/policies/reputation/migrations/0001_initial.py
similarity index 74%
rename from passbook/suspicious_policy/migrations/0001_initial.py
rename to passbook/policies/reputation/migrations/0001_initial.py
index f48ae499c..ffddca276 100644
--- a/passbook/suspicious_policy/migrations/0001_initial.py
+++ b/passbook/policies/reputation/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-03-03 18:17
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.conf import settings
@@ -10,22 +10,22 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
+ ('passbook_core', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('passbook_core', '0016_auto_20190227_1355'),
]
operations = [
migrations.CreateModel(
- name='IPScore',
+ name='IPReputation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('ip', models.GenericIPAddressField()),
+ ('ip', models.GenericIPAddressField(unique=True)),
('score', models.IntegerField(default=0)),
('updated', models.DateTimeField(auto_now=True)),
],
),
migrations.CreateModel(
- name='SuspiciousRequestPolicy',
+ name='ReputationPolicy',
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')),
('check_ip', models.BooleanField(default=True)),
@@ -33,17 +33,18 @@ class Migration(migrations.Migration):
('threshold', models.IntegerField(default=-5)),
],
options={
- 'abstract': False,
+ 'verbose_name': 'Reputation Policy',
+ 'verbose_name_plural': 'Reputation Policies',
},
bases=('passbook_core.policy',),
),
migrations.CreateModel(
- name='UserScore',
+ name='UserReputation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('score', models.IntegerField(default=0)),
('updated', models.DateTimeField(auto_now=True)),
- ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
diff --git a/passbook/otp/migrations/__init__.py b/passbook/policies/reputation/migrations/__init__.py
similarity index 100%
rename from passbook/otp/migrations/__init__.py
rename to passbook/policies/reputation/migrations/__init__.py
diff --git a/passbook/suspicious_policy/models.py b/passbook/policies/reputation/models.py
similarity index 62%
rename from passbook/suspicious_policy/models.py
rename to passbook/policies/reputation/models.py
index a7a52501c..d41711dc5 100644
--- a/passbook/suspicious_policy/models.py
+++ b/passbook/policies/reputation/models.py
@@ -1,38 +1,40 @@
-"""passbook suspicious request policy"""
+"""passbook reputation request policy"""
from django.db import models
from django.utils.translation import gettext as _
from ipware import get_client_ip
from passbook.core.models import Policy, User
-from passbook.policy.struct import PolicyRequest, PolicyResult
+from passbook.policies.struct import PolicyRequest, PolicyResult
-class SuspiciousRequestPolicy(Policy):
+class ReputationPolicy(Policy):
"""Return true if request IP/target username's score is below a certain threshold"""
check_ip = models.BooleanField(default=True)
check_username = models.BooleanField(default=True)
threshold = models.IntegerField(default=-5)
- form = 'passbook.suspicious_policy.forms.SuspiciousRequestPolicyForm'
+ form = 'passbook.policies.reputation.forms.ReputationPolicyForm'
def passes(self, request: PolicyRequest) -> PolicyResult:
remote_ip, _ = get_client_ip(request.http_request)
passing = True
if self.check_ip:
- ip_scores = IPScore.objects.filter(ip=remote_ip, score__lte=self.threshold)
+ ip_scores = IPReputation.objects.filter(ip=remote_ip, score__lte=self.threshold)
passing = passing and ip_scores.exists()
if self.check_username:
- user_scores = UserScore.objects.filter(user=request.user, score__lte=self.threshold)
+ user_scores = UserReputation.objects.filter(user=request.user,
+ score__lte=self.threshold)
passing = passing and user_scores.exists()
return PolicyResult(passing)
class Meta:
- verbose_name = _('Suspicious Request Policy')
- verbose_name_plural = _('Suspicious Request Policies')
+ verbose_name = _('Reputation Policy')
+ verbose_name_plural = _('Reputation Policies')
-class IPScore(models.Model):
+
+class IPReputation(models.Model):
"""Store score coming from the same IP"""
ip = models.GenericIPAddressField(unique=True)
@@ -40,9 +42,10 @@ class IPScore(models.Model):
updated = models.DateTimeField(auto_now=True)
def __str__(self):
- return "IPScore for %s @ %d" % (self.ip, self.score)
+ return f"IPReputation for {self.ip} @ {self.score}"
-class UserScore(models.Model):
+
+class UserReputation(models.Model):
"""Store score attempting to log in as the same username"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
@@ -50,4 +53,4 @@ class UserScore(models.Model):
updated = models.DateTimeField(auto_now=True)
def __str__(self):
- return "UserScore for %s @ %d" % (self.user, self.score)
+ return f"UserReputation for {self.user} @ {self.score}"
diff --git a/passbook/suspicious_policy/signals.py b/passbook/policies/reputation/signals.py
similarity index 84%
rename from passbook/suspicious_policy/signals.py
rename to passbook/policies/reputation/signals.py
index a0ca5058a..dea151573 100644
--- a/passbook/suspicious_policy/signals.py
+++ b/passbook/policies/reputation/signals.py
@@ -1,11 +1,11 @@
-"""passbook suspicious request signals"""
+"""passbook reputation request signals"""
from django.contrib.auth.signals import user_logged_in, user_login_failed
from django.dispatch import receiver
from ipware import get_client_ip
from structlog import get_logger
from passbook.core.models import User
-from passbook.suspicious_policy.models import IPScore, UserScore
+from passbook.policies.reputation.models import IPReputation, UserReputation
LOGGER = get_logger()
@@ -25,14 +25,14 @@ def get_remote_ip(request):
def update_score(request, username, amount):
"""Update score for IP and User"""
remote_ip = get_remote_ip(request)
- ip_score, _ = IPScore.objects.update_or_create(ip=remote_ip)
+ ip_score, _ = IPReputation.objects.update_or_create(ip=remote_ip)
ip_score.score += amount
ip_score.save()
LOGGER.debug("Updated score", amount=amount, for_ip=remote_ip)
user = User.objects.filter(username=username)
if not user.exists():
return
- user_score, _ = UserScore.objects.update_or_create(user=user.first())
+ user_score, _ = UserReputation.objects.update_or_create(user=user.first())
user_score.score += amount
user_score.save()
LOGGER.debug("Updated score", amount=amount, for_user=username)
diff --git a/passbook/password_expiry_policy/__init__.py b/passbook/policies/sso/__init__.py
similarity index 100%
rename from passbook/password_expiry_policy/__init__.py
rename to passbook/policies/sso/__init__.py
diff --git a/passbook/policies/sso/admin.py b/passbook/policies/sso/admin.py
new file mode 100644
index 000000000..a5146d89f
--- /dev/null
+++ b/passbook/policies/sso/admin.py
@@ -0,0 +1,4 @@
+"""autodiscover admin"""
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_policies_sso')
diff --git a/passbook/policies/sso/apps.py b/passbook/policies/sso/apps.py
new file mode 100644
index 000000000..18ae2fc53
--- /dev/null
+++ b/passbook/policies/sso/apps.py
@@ -0,0 +1,11 @@
+"""passbook sso policy app config"""
+
+from django.apps import AppConfig
+
+
+class PassbookPoliciesSSOConfig(AppConfig):
+ """passbook sso policy app config"""
+
+ name = 'passbook.policies.sso'
+ label = 'passbook_policies_sso'
+ verbose_name = 'passbook Policies.SSO'
diff --git a/passbook/policies/sso/forms.py b/passbook/policies/sso/forms.py
new file mode 100644
index 000000000..52fc0e50b
--- /dev/null
+++ b/passbook/policies/sso/forms.py
@@ -0,0 +1,19 @@
+"""passbook Policy forms"""
+
+from django import forms
+
+from passbook.policies.forms import GENERAL_FIELDS
+from passbook.policies.sso.models import SSOLoginPolicy
+
+
+class SSOLoginPolicyForm(forms.ModelForm):
+ """Edit SSOLoginPolicy instances"""
+
+ class Meta:
+
+ model = SSOLoginPolicy
+ fields = GENERAL_FIELDS
+ widgets = {
+ 'name': forms.TextInput(),
+ 'order': forms.NumberInput(),
+ }
diff --git a/passbook/core/migrations/0024_ssologinpolicy.py b/passbook/policies/sso/migrations/0001_initial.py
similarity index 85%
rename from passbook/core/migrations/0024_ssologinpolicy.py
rename to passbook/policies/sso/migrations/0001_initial.py
index f783d2841..4d0a3cf7d 100644
--- a/passbook/core/migrations/0024_ssologinpolicy.py
+++ b/passbook/policies/sso/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2 on 2019-04-29 21:14
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
@@ -6,8 +6,10 @@ from django.db import migrations, models
class Migration(migrations.Migration):
+ initial = True
+
dependencies = [
- ('passbook_core', '0023_remove_user_applications'),
+ ('passbook_core', '0001_initial'),
]
operations = [
diff --git a/passbook/password_expiry_policy/migrations/__init__.py b/passbook/policies/sso/migrations/__init__.py
similarity index 100%
rename from passbook/password_expiry_policy/migrations/__init__.py
rename to passbook/policies/sso/migrations/__init__.py
diff --git a/passbook/policies/sso/models.py b/passbook/policies/sso/models.py
new file mode 100644
index 000000000..560d2597c
--- /dev/null
+++ b/passbook/policies/sso/models.py
@@ -0,0 +1,22 @@
+"""sso models"""
+from django.utils.translation import gettext as _
+
+from passbook.core.models import Policy
+from passbook.policies.struct import PolicyRequest, PolicyResult
+
+
+class SSOLoginPolicy(Policy):
+ """Policy that applies to users that have authenticated themselves through SSO"""
+
+ form = 'passbook.policies.sso.forms.SSOLoginPolicyForm'
+
+ def passes(self, request: PolicyRequest) -> PolicyResult:
+ """Check if user instance passes this policy"""
+ from passbook.factors.view import AuthenticationView
+ is_sso_login = request.user.session.get(AuthenticationView.SESSION_IS_SSO_LOGIN, False)
+ return PolicyResult(is_sso_login)
+
+ class Meta:
+
+ verbose_name = _('SSO Login Policy')
+ verbose_name_plural = _('SSO Login Policies')
diff --git a/passbook/policy/struct.py b/passbook/policies/struct.py
similarity index 88%
rename from passbook/policy/struct.py
rename to passbook/policies/struct.py
index 0d13f00d3..83add6e3d 100644
--- a/passbook/policy/struct.py
+++ b/passbook/policies/struct.py
@@ -1,4 +1,5 @@
"""policy structs"""
+from __future__ import annotations
from typing import TYPE_CHECKING, List
from django.http import HttpRequest
@@ -9,10 +10,10 @@ if TYPE_CHECKING:
class PolicyRequest:
"""Data-class to hold policy request data"""
- user: 'User'
+ user: User
http_request: HttpRequest
- def __init__(self, user: 'User'):
+ def __init__(self, user: User):
self.user = user
def __str__(self):
diff --git a/passbook/policy/__init__.py b/passbook/policies/webhook/__init__.py
similarity index 100%
rename from passbook/policy/__init__.py
rename to passbook/policies/webhook/__init__.py
diff --git a/passbook/policies/webhook/admin.py b/passbook/policies/webhook/admin.py
new file mode 100644
index 000000000..cf14a6054
--- /dev/null
+++ b/passbook/policies/webhook/admin.py
@@ -0,0 +1,4 @@
+"""autodiscover admin"""
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_policies_webhook')
diff --git a/passbook/policies/webhook/apps.py b/passbook/policies/webhook/apps.py
new file mode 100644
index 000000000..38738930d
--- /dev/null
+++ b/passbook/policies/webhook/apps.py
@@ -0,0 +1,11 @@
+"""passbook Webhook policy app config"""
+
+from django.apps import AppConfig
+
+
+class PassbookPoliciesWebhookConfig(AppConfig):
+ """passbook Webhook policy app config"""
+
+ name = 'passbook.policies.webhook'
+ label = 'passbook_policies_webhook'
+ verbose_name = 'passbook Policies.Webhook'
diff --git a/passbook/policies/webhook/forms.py b/passbook/policies/webhook/forms.py
new file mode 100644
index 000000000..bbf7f1f5a
--- /dev/null
+++ b/passbook/policies/webhook/forms.py
@@ -0,0 +1,23 @@
+"""passbook Policy forms"""
+
+from django import forms
+
+from passbook.policies.forms import GENERAL_FIELDS
+from passbook.policies.webhook.models import WebhookPolicy
+
+
+class WebhookPolicyForm(forms.ModelForm):
+ """WebhookPolicyForm Form"""
+
+ class Meta:
+
+ model = WebhookPolicy
+ fields = GENERAL_FIELDS + ['url', 'method', 'json_body', 'json_headers',
+ 'result_jsonpath', 'result_json_value', ]
+ widgets = {
+ 'name': forms.TextInput(),
+ 'json_body': forms.TextInput(),
+ 'json_headers': forms.TextInput(),
+ 'result_jsonpath': forms.TextInput(),
+ 'result_json_value': forms.TextInput(),
+ }
diff --git a/passbook/policies/webhook/migrations/0001_initial.py b/passbook/policies/webhook/migrations/0001_initial.py
new file mode 100644
index 000000000..871215c8f
--- /dev/null
+++ b/passbook/policies/webhook/migrations/0001_initial.py
@@ -0,0 +1,33 @@
+# Generated by Django 2.2.6 on 2019-10-07 14:07
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('passbook_core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='WebhookPolicy',
+ 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')),
+ ('url', models.URLField()),
+ ('method', models.CharField(choices=[('GET', 'GET'), ('POST', 'POST'), ('PATCH', 'PATCH'), ('DELETE', 'DELETE'), ('PUT', 'PUT')], max_length=10)),
+ ('json_body', models.TextField()),
+ ('json_headers', models.TextField()),
+ ('result_jsonpath', models.TextField()),
+ ('result_json_value', models.TextField()),
+ ],
+ options={
+ 'verbose_name': 'Webhook Policy',
+ 'verbose_name_plural': 'Webhook Policies',
+ },
+ bases=('passbook_core.policy',),
+ ),
+ ]
diff --git a/passbook/pretend/__init__.py b/passbook/policies/webhook/migrations/__init__.py
similarity index 100%
rename from passbook/pretend/__init__.py
rename to passbook/policies/webhook/migrations/__init__.py
diff --git a/passbook/policies/webhook/models.py b/passbook/policies/webhook/models.py
new file mode 100644
index 000000000..91cf59608
--- /dev/null
+++ b/passbook/policies/webhook/models.py
@@ -0,0 +1,42 @@
+"""webhook models"""
+from django.db import models
+from django.utils.translation import gettext as _
+
+from passbook.core.models import Policy
+from passbook.policies.struct import PolicyRequest, PolicyResult
+
+
+class WebhookPolicy(Policy):
+ """Policy that asks webhook"""
+
+ METHOD_GET = 'GET'
+ METHOD_POST = 'POST'
+ METHOD_PATCH = 'PATCH'
+ METHOD_DELETE = 'DELETE'
+ METHOD_PUT = 'PUT'
+
+ METHODS = (
+ (METHOD_GET, METHOD_GET),
+ (METHOD_POST, METHOD_POST),
+ (METHOD_PATCH, METHOD_PATCH),
+ (METHOD_DELETE, METHOD_DELETE),
+ (METHOD_PUT, METHOD_PUT),
+ )
+
+ url = models.URLField()
+ method = models.CharField(max_length=10, choices=METHODS)
+ json_body = models.TextField()
+ json_headers = models.TextField()
+ result_jsonpath = models.TextField()
+ result_json_value = models.TextField()
+
+ form = 'passbook.policies.webhook.forms.WebhookPolicyForm'
+
+ def passes(self, request: PolicyRequest) -> PolicyResult:
+ """Call webhook asynchronously and report back"""
+ raise NotImplementedError()
+
+ class Meta:
+
+ verbose_name = _('Webhook Policy')
+ verbose_name_plural = _('Webhook Policies')
diff --git a/passbook/pretend/apps.py b/passbook/pretend/apps.py
deleted file mode 100644
index 3a823b13f..000000000
--- a/passbook/pretend/apps.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""passbook pretend config"""
-from django.apps import AppConfig
-
-
-class PassbookPretendConfig(AppConfig):
- """passbook pretend config"""
-
- name = 'passbook.pretend'
- label = 'passbook_pretend'
- verbose_name = 'passbook Pretender'
- mountpoint = ''
diff --git a/passbook/pretend/urls.py b/passbook/pretend/urls.py
deleted file mode 100644
index e140c9f9a..000000000
--- a/passbook/pretend/urls.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""passbook pretend urls"""
-from django.urls import include, path
-from oauth2_provider.views import TokenView
-
-from passbook.oauth_provider.views.oauth2 import PassbookAuthorizationView
-from passbook.pretend.views.github import GitHubUserView
-
-github_urlpatterns = [
- path('login/oauth/authorize', PassbookAuthorizationView.as_view(), name='github-authorize'),
- path('login/oauth/access_token', TokenView.as_view(), name='github-access-token'),
- path('user', GitHubUserView.as_view(), name='github-user'),
-]
-
-urlpatterns = [
- path('', include(github_urlpatterns))
-]
diff --git a/passbook/pretend/views/__init__.py b/passbook/providers/__init__.py
similarity index 100%
rename from passbook/pretend/views/__init__.py
rename to passbook/providers/__init__.py
diff --git a/passbook/saml_idp/__init__.py b/passbook/providers/app_gw/__init__.py
similarity index 100%
rename from passbook/saml_idp/__init__.py
rename to passbook/providers/app_gw/__init__.py
diff --git a/passbook/app_gw/admin.py b/passbook/providers/app_gw/admin.py
similarity index 69%
rename from passbook/app_gw/admin.py
rename to passbook/providers/app_gw/admin.py
index 9391932a6..80e51f8b4 100644
--- a/passbook/app_gw/admin.py
+++ b/passbook/providers/app_gw/admin.py
@@ -2,4 +2,4 @@
from passbook.lib.admin import admin_autoregister
-admin_autoregister('passbook_app_gw')
+admin_autoregister('passbook_providers_app_gw')
diff --git a/passbook/providers/app_gw/apps.py b/passbook/providers/app_gw/apps.py
new file mode 100644
index 000000000..0e5c803e9
--- /dev/null
+++ b/passbook/providers/app_gw/apps.py
@@ -0,0 +1,11 @@
+"""passbook Application Security Gateway app"""
+from django.apps import AppConfig
+
+
+class PassbookApplicationApplicationGatewayConfig(AppConfig):
+ """passbook app_gw app"""
+
+ name = 'passbook.providers.app_gw'
+ label = 'passbook_providers_app_gw'
+ verbose_name = 'passbook Providers.Application Security Gateway'
+ mountpoint = 'application/gateway/'
diff --git a/passbook/app_gw/forms.py b/passbook/providers/app_gw/forms.py
similarity index 94%
rename from passbook/app_gw/forms.py
rename to passbook/providers/app_gw/forms.py
index fb3df1507..23eadd2d6 100644
--- a/passbook/app_gw/forms.py
+++ b/passbook/providers/app_gw/forms.py
@@ -6,8 +6,9 @@ from django.contrib.admin.widgets import FilteredSelectMultiple
from django.forms import ValidationError
from django.utils.translation import gettext as _
-from passbook.app_gw.models import ApplicationGatewayProvider, RewriteRule
from passbook.lib.fields import DynamicArrayField
+from passbook.providers.app_gw.models import (ApplicationGatewayProvider,
+ RewriteRule)
class ApplicationGatewayProviderForm(forms.ModelForm):
diff --git a/passbook/providers/app_gw/middleware.py b/passbook/providers/app_gw/middleware.py
new file mode 100644
index 000000000..9aa94c506
--- /dev/null
+++ b/passbook/providers/app_gw/middleware.py
@@ -0,0 +1,55 @@
+import time
+
+from django.conf import settings
+from django.contrib.sessions.middleware import SessionMiddleware
+from django.utils.cache import patch_vary_headers
+from django.utils.http import cookie_date
+from structlog import get_logger
+
+from passbook.factors.view import AuthenticationView
+
+LOGGER = get_logger()
+
+class SessionHostDomainMiddleware(SessionMiddleware):
+
+ def process_request(self, request):
+ session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
+ request.session = self.SessionStore(session_key)
+
+ def process_response(self, request, response):
+ """
+ If request.session was modified, or if the configuration is to save the
+ session every time, save the changes and set a session cookie.
+ """
+ try:
+ accessed = request.session.accessed
+ modified = request.session.modified
+ except AttributeError:
+ pass
+ else:
+ if accessed:
+ patch_vary_headers(response, ('Cookie',))
+ if modified or settings.SESSION_SAVE_EVERY_REQUEST:
+ if request.session.get_expire_at_browser_close():
+ max_age = None
+ expires = None
+ else:
+ max_age = request.session.get_expiry_age()
+ expires_time = time.time() + max_age
+ expires = cookie_date(expires_time)
+ # Save the session data and refresh the client cookie.
+ # Skip session save for 500 responses, refs #3881.
+ if response.status_code != 500:
+ request.session.save()
+ hosts = [request.get_host().split(':')[0]]
+ if AuthenticationView.SESSION_FORCE_COOKIE_HOSTNAME in request.session:
+ hosts.append(request.session[AuthenticationView.SESSION_FORCE_COOKIE_HOSTNAME])
+ LOGGER.debug("Setting hosts for session", hosts=hosts)
+ for host in hosts:
+ response.set_cookie(settings.SESSION_COOKIE_NAME,
+ request.session.session_key, max_age=max_age,
+ expires=expires, domain=host,
+ path=settings.SESSION_COOKIE_PATH,
+ secure=settings.SESSION_COOKIE_SECURE or None,
+ httponly=settings.SESSION_COOKIE_HTTPONLY or None)
+ return response
diff --git a/passbook/app_gw/migrations/0001_initial.py b/passbook/providers/app_gw/migrations/0001_initial.py
similarity index 87%
rename from passbook/app_gw/migrations/0001_initial.py
rename to passbook/providers/app_gw/migrations/0001_initial.py
index bdbf88084..3bcdb5907 100644
--- a/passbook/app_gw/migrations/0001_initial.py
+++ b/passbook/providers/app_gw/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-03-20 21:38
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.contrib.postgres.fields
import django.db.models.deletion
@@ -10,7 +10,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('passbook_core', '0020_groupmembershippolicy'),
+ ('passbook_core', '0001_initial'),
]
operations = [
@@ -21,7 +21,7 @@ class Migration(migrations.Migration):
('server_name', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)),
('upstream', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)),
('enabled', models.BooleanField(default=True)),
- ('authentication_header', models.TextField(default='X-Remote-User')),
+ ('authentication_header', models.TextField(blank=True, default='X-Remote-User')),
('default_content_type', models.TextField(default='application/octet-stream')),
('upstream_ssl_verification', models.BooleanField(default=True)),
],
@@ -39,7 +39,7 @@ class Migration(migrations.Migration):
('halt', models.BooleanField(default=False)),
('replacement', models.TextField()),
('redirect', models.CharField(choices=[('internal', 'Internal'), (301, 'Moved Permanently'), (302, 'Found')], max_length=50)),
- ('conditions', models.ManyToManyField(to='passbook_core.Policy')),
+ ('conditions', models.ManyToManyField(blank=True, to='passbook_core.Policy')),
],
options={
'verbose_name': 'Rewrite Rule',
diff --git a/passbook/saml_idp/migrations/__init__.py b/passbook/providers/app_gw/migrations/__init__.py
similarity index 100%
rename from passbook/saml_idp/migrations/__init__.py
rename to passbook/providers/app_gw/migrations/__init__.py
diff --git a/passbook/app_gw/models.py b/passbook/providers/app_gw/models.py
similarity index 94%
rename from passbook/app_gw/models.py
rename to passbook/providers/app_gw/models.py
index dec46bdc0..febb7c9e6 100644
--- a/passbook/app_gw/models.py
+++ b/passbook/providers/app_gw/models.py
@@ -19,7 +19,7 @@ class ApplicationGatewayProvider(Provider):
default_content_type = models.TextField(default='application/octet-stream')
upstream_ssl_verification = models.BooleanField(default=True)
- form = 'passbook.app_gw.forms.ApplicationGatewayProviderForm'
+ form = 'passbook.providers.app_gw.forms.ApplicationGatewayProviderForm'
@property
def name(self):
@@ -54,7 +54,7 @@ class RewriteRule(PropertyMapping):
replacement = models.TextField() # python formatted strings, use {match.1}
redirect = models.CharField(max_length=50, choices=REDIRECTS)
- form = 'passbook.app_gw.forms.RewriteRuleForm'
+ form = 'passbook.providers.app_gw.forms.RewriteRuleForm'
_matcher = None
diff --git a/passbook/saml_idp/processors/__init__.py b/passbook/providers/app_gw/provider/__init__.py
similarity index 100%
rename from passbook/saml_idp/processors/__init__.py
rename to passbook/providers/app_gw/provider/__init__.py
diff --git a/passbook/suspicious_policy/__init__.py b/passbook/providers/app_gw/provider/kubernetes/__init__.py
similarity index 100%
rename from passbook/suspicious_policy/__init__.py
rename to passbook/providers/app_gw/provider/kubernetes/__init__.py
diff --git a/passbook/providers/app_gw/urls.py b/passbook/providers/app_gw/urls.py
new file mode 100644
index 000000000..62cbc1ed9
--- /dev/null
+++ b/passbook/providers/app_gw/urls.py
@@ -0,0 +1,8 @@
+"""passbook app_gw urls"""
+from django.urls import path
+
+from passbook.providers.app_gw.views import NginxCheckView
+
+urlpatterns = [
+ path('nginx/', NginxCheckView.as_view())
+]
diff --git a/passbook/providers/app_gw/views.py b/passbook/providers/app_gw/views.py
new file mode 100644
index 000000000..5090bf61f
--- /dev/null
+++ b/passbook/providers/app_gw/views.py
@@ -0,0 +1,36 @@
+"""passbook app_gw views"""
+from pprint import pprint
+from urllib.parse import urlparse
+
+from django.http import HttpRequest, HttpResponse
+from django.views import View
+from structlog import get_logger
+
+from passbook.core.views.access import AccessMixin
+from passbook.providers.app_gw.models import ApplicationGatewayProvider
+
+ORIGINAL_URL = 'HTTP_X_ORIGINAL_URL'
+LOGGER = get_logger()
+
+
+class NginxCheckView(AccessMixin, View):
+
+ def dispatch(self, request: HttpRequest) -> HttpResponse:
+ pprint(request.META)
+ parsed_url = urlparse(request.META.get(ORIGINAL_URL))
+ # request.session[AuthenticationView.SESSION_ALLOW_ABSOLUTE_NEXT] = True
+ # request.session[AuthenticationView.SESSION_FORCE_COOKIE_HOSTNAME] = parsed_url.hostname
+ print(request.user)
+ if not request.user.is_authenticated:
+ return HttpResponse(status=401)
+ matching = ApplicationGatewayProvider.objects.filter(
+ server_name__contains=[parsed_url.hostname])
+ if not matching.exists():
+ LOGGER.debug("Couldn't find matching application", host=parsed_url.hostname)
+ return HttpResponse(status=403)
+ application = self.provider_to_application(matching.first())
+ has_access, _ = self.user_has_access(application, request.user)
+ if has_access:
+ return HttpResponse(status=202)
+ LOGGER.debug("User not passing", user=request.user)
+ return HttpResponse(status=401)
diff --git a/passbook/suspicious_policy/migrations/__init__.py b/passbook/providers/oauth/__init__.py
similarity index 100%
rename from passbook/suspicious_policy/migrations/__init__.py
rename to passbook/providers/oauth/__init__.py
diff --git a/passbook/providers/oauth/admin.py b/passbook/providers/oauth/admin.py
new file mode 100644
index 000000000..6f0f106db
--- /dev/null
+++ b/passbook/providers/oauth/admin.py
@@ -0,0 +1,4 @@
+"""oauth provider admin"""
+# from passbook.lib.admin import admin_autoregister
+
+# admin_autoregister('passbook_providers_oauth')
diff --git a/passbook/oauth_provider/apps.py b/passbook/providers/oauth/apps.py
similarity index 50%
rename from passbook/oauth_provider/apps.py
rename to passbook/providers/oauth/apps.py
index d29c696fc..2974f1223 100644
--- a/passbook/oauth_provider/apps.py
+++ b/passbook/providers/oauth/apps.py
@@ -3,10 +3,10 @@
from django.apps import AppConfig
-class PassbookOAuthProviderConfig(AppConfig):
+class PassbookProviderOAuthConfig(AppConfig):
"""passbook auth oauth provider app config"""
- name = 'passbook.oauth_provider'
- label = 'passbook_oauth_provider'
- verbose_name = 'passbook OAuth Provider'
+ name = 'passbook.providers.oauth'
+ label = 'passbook_providers_oauth'
+ verbose_name = 'passbook Providers.OAuth'
mountpoint = 'application/oauth/'
diff --git a/passbook/oauth_provider/forms.py b/passbook/providers/oauth/forms.py
similarity index 84%
rename from passbook/oauth_provider/forms.py
rename to passbook/providers/oauth/forms.py
index 895291ff6..dffca09d5 100644
--- a/passbook/oauth_provider/forms.py
+++ b/passbook/providers/oauth/forms.py
@@ -2,7 +2,7 @@
from django import forms
-from passbook.oauth_provider.models import OAuth2Provider
+from passbook.providers.oauth.models import OAuth2Provider
class OAuth2ProviderForm(forms.ModelForm):
diff --git a/passbook/oauth_provider/locale/de/LC_MESSAGES/django.po b/passbook/providers/oauth/locale/de/LC_MESSAGES/django.po
similarity index 100%
rename from passbook/oauth_provider/locale/de/LC_MESSAGES/django.po
rename to passbook/providers/oauth/locale/de/LC_MESSAGES/django.po
diff --git a/passbook/oauth_provider/locale/en/LC_MESSAGES/django.po b/passbook/providers/oauth/locale/en/LC_MESSAGES/django.po
similarity index 100%
rename from passbook/oauth_provider/locale/en/LC_MESSAGES/django.po
rename to passbook/providers/oauth/locale/en/LC_MESSAGES/django.po
diff --git a/passbook/oauth_provider/locale/es/LC_MESSAGES/django.po b/passbook/providers/oauth/locale/es/LC_MESSAGES/django.po
similarity index 100%
rename from passbook/oauth_provider/locale/es/LC_MESSAGES/django.po
rename to passbook/providers/oauth/locale/es/LC_MESSAGES/django.po
diff --git a/passbook/oauth_provider/locale/fr/LC_MESSAGES/django.po b/passbook/providers/oauth/locale/fr/LC_MESSAGES/django.po
similarity index 100%
rename from passbook/oauth_provider/locale/fr/LC_MESSAGES/django.po
rename to passbook/providers/oauth/locale/fr/LC_MESSAGES/django.po
diff --git a/passbook/oauth_provider/migrations/0001_initial.py b/passbook/providers/oauth/migrations/0001_initial.py
similarity index 94%
rename from passbook/oauth_provider/migrations/0001_initial.py
rename to passbook/providers/oauth/migrations/0001_initial.py
index b25fedbca..98174e732 100644
--- a/passbook/oauth_provider/migrations/0001_initial.py
+++ b/passbook/providers/oauth/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-02-16 09:13
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
import oauth2_provider.generators
@@ -33,7 +33,7 @@ class Migration(migrations.Migration):
('skip_authorization', models.BooleanField(default=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
- ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='passbook_oauth_provider_oauth2provider', to=settings.AUTH_USER_MODEL)),
+ ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='passbook_providers_oauth_oauth2provider', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'OAuth2 Provider',
diff --git a/passbook/providers/oauth/migrations/__init__.py b/passbook/providers/oauth/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/oauth_provider/models.py b/passbook/providers/oauth/models.py
similarity index 79%
rename from passbook/oauth_provider/models.py
rename to passbook/providers/oauth/models.py
index 9dff16202..9f148e57f 100644
--- a/passbook/oauth_provider/models.py
+++ b/passbook/providers/oauth/models.py
@@ -10,19 +10,19 @@ from passbook.core.models import Provider
class OAuth2Provider(Provider, AbstractApplication):
"""Associate an OAuth2 Application with a Product"""
- form = 'passbook.oauth_provider.forms.OAuth2ProviderForm'
+ form = 'passbook.providers.oauth.forms.OAuth2ProviderForm'
def __str__(self):
- return "OAuth2 Provider %s" % self.name
+ return f"OAuth2 Provider {self.name}"
def html_setup_urls(self, request):
"""return template and context modal with URLs for authorize, token, openid-config, etc"""
return "oauth2_provider/setup_url_modal.html", {
'provider': self,
'authorize_url': request.build_absolute_uri(
- reverse('passbook_oauth_provider:oauth2-authorize')),
+ reverse('passbook_providers_oauth:oauth2-authorize')),
'token_url': request.build_absolute_uri(
- reverse('passbook_oauth_provider:token')),
+ reverse('passbook_providers_oauth:token')),
'userinfo_url': request.build_absolute_uri(
reverse('passbook_api:openid')),
}
diff --git a/passbook/oauth_provider/settings.py b/passbook/providers/oauth/settings.py
similarity index 90%
rename from passbook/oauth_provider/settings.py
rename to passbook/providers/oauth/settings.py
index 7d7178119..f8b077bc1 100644
--- a/passbook/oauth_provider/settings.py
+++ b/passbook/providers/oauth/settings.py
@@ -15,7 +15,7 @@ AUTHENTICATION_BACKENDS = [
'oauth2_provider.backends.OAuth2Backend',
]
-OAUTH2_PROVIDER_APPLICATION_MODEL = 'passbook_oauth_provider.OAuth2Provider'
+OAUTH2_PROVIDER_APPLICATION_MODEL = 'passbook_providers_oauth.OAuth2Provider'
OAUTH2_PROVIDER = {
# this is the list of available scopes
diff --git a/passbook/oauth_provider/templates/oauth2_provider/authorize.html b/passbook/providers/oauth/templates/oauth2_provider/authorize.html
similarity index 100%
rename from passbook/oauth_provider/templates/oauth2_provider/authorize.html
rename to passbook/providers/oauth/templates/oauth2_provider/authorize.html
diff --git a/passbook/oauth_provider/templates/oauth2_provider/base.html b/passbook/providers/oauth/templates/oauth2_provider/base.html
similarity index 100%
rename from passbook/oauth_provider/templates/oauth2_provider/base.html
rename to passbook/providers/oauth/templates/oauth2_provider/base.html
diff --git a/passbook/oauth_provider/templates/oauth2_provider/setup_url_modal.html b/passbook/providers/oauth/templates/oauth2_provider/setup_url_modal.html
similarity index 100%
rename from passbook/oauth_provider/templates/oauth2_provider/setup_url_modal.html
rename to passbook/providers/oauth/templates/oauth2_provider/setup_url_modal.html
diff --git a/passbook/oauth_provider/urls.py b/passbook/providers/oauth/urls.py
similarity index 63%
rename from passbook/oauth_provider/urls.py
rename to passbook/providers/oauth/urls.py
index 1fd14cc2d..854569116 100644
--- a/passbook/oauth_provider/urls.py
+++ b/passbook/providers/oauth/urls.py
@@ -3,7 +3,7 @@
from django.urls import path
from oauth2_provider import views
-from passbook.oauth_provider.views import oauth2
+from passbook.providers.oauth.views import github, oauth2
urlpatterns = [
# Custom OAuth 2 Authorize View
@@ -18,3 +18,14 @@ urlpatterns = [
path("revoke_token/", views.RevokeTokenView.as_view(), name="revoke-token"),
path("introspect/", views.IntrospectTokenView.as_view(), name="introspect"),
]
+
+github_urlpatterns = [
+ path('login/oauth/authorize',
+ oauth2.PassbookAuthorizationView.as_view(), name='github-authorize'),
+ path('login/oauth/access_token', views.TokenView.as_view(), name='github-access-token'),
+ path('user', github.GitHubUserView.as_view(), name='github-user'),
+]
+
+# urlpatterns = [
+# path('', include(github_urlpatterns))
+# ]
diff --git a/passbook/providers/oauth/views/__init__.py b/passbook/providers/oauth/views/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/pretend/views/github.py b/passbook/providers/oauth/views/github.py
similarity index 100%
rename from passbook/pretend/views/github.py
rename to passbook/providers/oauth/views/github.py
diff --git a/passbook/oauth_provider/views/oauth2.py b/passbook/providers/oauth/views/oauth2.py
similarity index 88%
rename from passbook/oauth_provider/views/oauth2.py
rename to passbook/providers/oauth/views/oauth2.py
index e42886a0f..0ec663220 100644
--- a/passbook/oauth_provider/views/oauth2.py
+++ b/passbook/providers/oauth/views/oauth2.py
@@ -12,7 +12,7 @@ from passbook.audit.models import AuditEntry
from passbook.core.models import Application
from passbook.core.views.access import AccessMixin
from passbook.core.views.utils import LoadingView, PermissionDeniedView
-from passbook.oauth_provider.models import OAuth2Provider
+from passbook.providers.oauth.models import OAuth2Provider
LOGGER = get_logger()
@@ -24,7 +24,7 @@ class PassbookAuthorizationLoadingView(LoginRequiredMixin, LoadingView):
def get_url(self):
querystring = urlencode(self.request.GET)
- return reverse('passbook_oauth_provider:oauth2-ok-authorize')+'?'+querystring
+ return reverse('passbook_providers_oauth:oauth2-ok-authorize')+'?'+querystring
class OAuthPermissionDenied(PermissionDeniedView):
@@ -41,7 +41,7 @@ class PassbookAuthorizationView(AccessMixin, AuthorizationView):
LOGGER.debug("response_type not set, defaulting to 'code'")
querystring = urlencode(self.request.GET)
querystring += '&response_type=code'
- return redirect(reverse('passbook_oauth_provider:oauth2-ok-authorize') + '?' + querystring)
+ return redirect(reverse('passbook_providers_oauth:oauth2-ok-authorize') + '?' + querystring)
def dispatch(self, request, *args, **kwargs):
"""Update OAuth2Provider's skip_authorization state"""
@@ -51,7 +51,7 @@ class PassbookAuthorizationView(AccessMixin, AuthorizationView):
try:
application = self.provider_to_application(provider)
except Application.DoesNotExist:
- return redirect('passbook_oauth_provider:oauth2-permission-denied')
+ return redirect('passbook_providers_oauth:oauth2-permission-denied')
# Update field here so oauth-toolkit does work for us
provider.skip_authorization = application.skip_authorization
provider.save()
@@ -61,7 +61,7 @@ class PassbookAuthorizationView(AccessMixin, AuthorizationView):
if not passing:
for policy_message in policy_messages:
messages.error(request, policy_message)
- return redirect('passbook_oauth_provider:oauth2-permission-denied')
+ return redirect('passbook_providers_oauth:oauth2-permission-denied')
# Some clients don't pass response_type, so we default to code
if 'response_type' not in request.GET:
return self._inject_response_type()
diff --git a/passbook/providers/oidc/__init__.py b/passbook/providers/oidc/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/providers/oidc/admin.py b/passbook/providers/oidc/admin.py
new file mode 100644
index 000000000..0da5dad89
--- /dev/null
+++ b/passbook/providers/oidc/admin.py
@@ -0,0 +1,5 @@
+"""oidc provider admin"""
+
+from passbook.lib.admin import admin_autoregister
+
+admin_autoregister('passbook_providers_oidc')
diff --git a/passbook/oidc_provider/apps.py b/passbook/providers/oidc/apps.py
similarity index 84%
rename from passbook/oidc_provider/apps.py
rename to passbook/providers/oidc/apps.py
index 34130de33..0a8466b36 100644
--- a/passbook/oidc_provider/apps.py
+++ b/passbook/providers/oidc/apps.py
@@ -6,12 +6,12 @@ from structlog import get_logger
LOGGER = get_logger()
-class PassbookOIDCProviderConfig(AppConfig):
+class PassbookProviderOIDCConfig(AppConfig):
"""passbook auth oidc provider app config"""
- name = 'passbook.oidc_provider'
- label = 'passbook_oidc_provider'
- verbose_name = 'passbook OIDC Provider'
+ name = 'passbook.providers.oidc'
+ label = 'passbook_providers_oidc'
+ verbose_name = 'passbook Providers.OIDC'
def ready(self):
try:
diff --git a/passbook/oidc_provider/forms.py b/passbook/providers/oidc/forms.py
similarity index 95%
rename from passbook/oidc_provider/forms.py
rename to passbook/providers/oidc/forms.py
index 23651bc3a..cff2c21e0 100644
--- a/passbook/oidc_provider/forms.py
+++ b/passbook/providers/oidc/forms.py
@@ -5,7 +5,7 @@ from oauth2_provider.generators import (generate_client_id,
generate_client_secret)
from oidc_provider.models import Client
-from passbook.oidc_provider.models import OpenIDProvider
+from passbook.providers.oidc.models import OpenIDProvider
class OIDCProviderForm(forms.ModelForm):
diff --git a/passbook/oidc_provider/lib.py b/passbook/providers/oidc/lib.py
similarity index 73%
rename from passbook/oidc_provider/lib.py
rename to passbook/providers/oidc/lib.py
index 19b33eef5..ce82d100f 100644
--- a/passbook/oidc_provider/lib.py
+++ b/passbook/providers/oidc/lib.py
@@ -4,7 +4,7 @@ from django.shortcuts import redirect
from structlog import get_logger
from passbook.core.models import Application
-from passbook.policy.engine import PolicyEngine
+from passbook.policies.engine import PolicyEngine
LOGGER = get_logger()
@@ -15,8 +15,8 @@ def check_permissions(request, user, client):
try:
application = client.openidprovider.application
except Application.DoesNotExist:
- return redirect('passbook_oauth_provider:oauth2-permission-denied')
- LOGGER.debug("Checking permissions of %s on application %s...", user, application)
+ return redirect('passbook_providers_oauth:oauth2-permission-denied')
+ LOGGER.debug("Checking permissions for application", user=user, application=application)
policy_engine = PolicyEngine(application.policies.all())
policy_engine.for_user(user).with_request(request).build()
@@ -25,5 +25,5 @@ def check_permissions(request, user, client):
if not passing:
for policy_message in policy_messages:
messages.error(request, policy_message)
- return redirect('passbook_oauth_provider:oauth2-permission-denied')
+ return redirect('passbook_providers_oauth:oauth2-permission-denied')
return None
diff --git a/passbook/oidc_provider/migrations/0001_initial.py b/passbook/providers/oidc/migrations/0001_initial.py
similarity index 75%
rename from passbook/oidc_provider/migrations/0001_initial.py
rename to passbook/providers/oidc/migrations/0001_initial.py
index 0e810eed7..c9924dce3 100644
--- a/passbook/oidc_provider/migrations/0001_initial.py
+++ b/passbook/providers/oidc/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.3 on 2019-07-05 12:16
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
@@ -9,8 +9,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
+ ('passbook_core', '0001_initial'),
('oidc_provider', '0026_client_multiple_response_types'),
- ('passbook_core', '0024_ssologinpolicy'),
]
operations = [
@@ -20,6 +20,10 @@ class Migration(migrations.Migration):
('provider_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Provider')),
('oidc_client', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='oidc_provider.Client')),
],
+ options={
+ 'verbose_name': 'OpenID Provider',
+ 'verbose_name_plural': 'OpenID Providers',
+ },
bases=('passbook_core.provider',),
),
]
diff --git a/passbook/providers/oidc/migrations/__init__.py b/passbook/providers/oidc/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/oidc_provider/models.py b/passbook/providers/oidc/models.py
similarity index 96%
rename from passbook/oidc_provider/models.py
rename to passbook/providers/oidc/models.py
index 091b1ef8b..9dea3dc4a 100644
--- a/passbook/oidc_provider/models.py
+++ b/passbook/providers/oidc/models.py
@@ -15,7 +15,7 @@ class OpenIDProvider(Provider):
oidc_client = models.OneToOneField(Client, on_delete=models.CASCADE)
- form = 'passbook.oidc_provider.forms.OIDCProviderForm'
+ form = 'passbook.providers.oidc.forms.OIDCProviderForm'
@property
def name(self):
diff --git a/passbook/providers/oidc/settings.py b/passbook/providers/oidc/settings.py
new file mode 100644
index 000000000..6e27f4457
--- /dev/null
+++ b/passbook/providers/oidc/settings.py
@@ -0,0 +1,7 @@
+"""passbook OIDC Provider"""
+
+INSTALLED_APPS = [
+ 'oidc_provider',
+]
+
+OIDC_AFTER_USERLOGIN_HOOK = "passbook.providers.oidc.lib.check_permissions"
diff --git a/passbook/oidc_provider/templates/oidc_provider/authorize.html b/passbook/providers/oidc/templates/oidc_provider/authorize.html
similarity index 100%
rename from passbook/oidc_provider/templates/oidc_provider/authorize.html
rename to passbook/providers/oidc/templates/oidc_provider/authorize.html
diff --git a/passbook/oidc_provider/templates/oidc_provider/setup_url_modal.html b/passbook/providers/oidc/templates/oidc_provider/setup_url_modal.html
similarity index 100%
rename from passbook/oidc_provider/templates/oidc_provider/setup_url_modal.html
rename to passbook/providers/oidc/templates/oidc_provider/setup_url_modal.html
diff --git a/passbook/providers/saml/__init__.py b/passbook/providers/saml/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/saml_idp/admin.py b/passbook/providers/saml/admin.py
similarity index 61%
rename from passbook/saml_idp/admin.py
rename to passbook/providers/saml/admin.py
index b222d2128..8813bd64b 100644
--- a/passbook/saml_idp/admin.py
+++ b/passbook/providers/saml/admin.py
@@ -2,4 +2,4 @@
from passbook.lib.admin import admin_autoregister
-admin_autoregister('passbook_saml_idp')
+admin_autoregister('passbook_providers_saml')
diff --git a/passbook/saml_idp/apps.py b/passbook/providers/saml/apps.py
similarity index 64%
rename from passbook/saml_idp/apps.py
rename to passbook/providers/saml/apps.py
index 12b146238..8aeb55316 100644
--- a/passbook/saml_idp/apps.py
+++ b/passbook/providers/saml/apps.py
@@ -2,24 +2,22 @@
from importlib import import_module
from django.apps import AppConfig
+from django.conf import settings
from structlog import get_logger
-from passbook.lib.config import CONFIG
-
LOGGER = get_logger()
-class PassbookSAMLIDPConfig(AppConfig):
+class PassbookProviderSAMLConfig(AppConfig):
"""passbook saml_idp app config"""
- name = 'passbook.saml_idp'
- label = 'passbook_saml_idp'
- verbose_name = 'passbook SAML IDP'
+ name = 'passbook.providers.saml'
+ label = 'passbook_providers_saml'
+ verbose_name = 'passbook Providers.SAML'
mountpoint = 'application/saml/'
def ready(self):
"""Load source_types from config file"""
- source_types_to_load = CONFIG.y('saml_idp.types', [])
- for source_type in source_types_to_load:
+ for source_type in settings.PASSBOOK_PROVIDERS_SAML_PROCESSORS:
try:
import_module(source_type)
LOGGER.info("Loaded SAML Processor", processor_class=source_type)
diff --git a/passbook/saml_idp/base.py b/passbook/providers/saml/base.py
similarity index 98%
rename from passbook/saml_idp/base.py
rename to passbook/providers/saml/base.py
index 1fd182928..e70329c4f 100644
--- a/passbook/saml_idp/base.py
+++ b/passbook/providers/saml/base.py
@@ -6,7 +6,7 @@ import uuid
from defusedxml import ElementTree
from structlog import get_logger
-from passbook.saml_idp import exceptions, utils, xml_render
+from passbook.providers.saml import exceptions, utils, xml_render
MINUTES = 60
HOURS = 60 * MINUTES
@@ -165,7 +165,7 @@ class Processor:
'Value': self._django_request.user.username,
},
]
- from passbook.saml_idp.models import SAMLPropertyMapping
+ from passbook.providers.saml.models import SAMLPropertyMapping
for mapping in self._remote.property_mappings.all().select_subclasses():
if isinstance(mapping, SAMLPropertyMapping):
mapping_payload = {
diff --git a/passbook/saml_idp/exceptions.py b/passbook/providers/saml/exceptions.py
similarity index 100%
rename from passbook/saml_idp/exceptions.py
rename to passbook/providers/saml/exceptions.py
diff --git a/passbook/saml_idp/forms.py b/passbook/providers/saml/forms.py
similarity index 89%
rename from passbook/saml_idp/forms.py
rename to passbook/providers/saml/forms.py
index 6f04d5344..dc2ea0f72 100644
--- a/passbook/saml_idp/forms.py
+++ b/passbook/providers/saml/forms.py
@@ -5,9 +5,9 @@ from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext as _
from passbook.lib.fields import DynamicArrayField
-from passbook.saml_idp.models import (SAMLPropertyMapping, SAMLProvider,
- get_provider_choices)
-from passbook.saml_idp.utils import CertificateBuilder
+from passbook.providers.saml.models import (SAMLPropertyMapping, SAMLProvider,
+ get_provider_choices)
+from passbook.providers.saml.utils import CertificateBuilder
class SAMLProviderForm(forms.ModelForm):
diff --git a/passbook/saml_idp/migrations/0001_initial.py b/passbook/providers/saml/migrations/0001_initial.py
similarity index 55%
rename from passbook/saml_idp/migrations/0001_initial.py
rename to passbook/providers/saml/migrations/0001_initial.py
index 4a210df76..8839bb5a9 100644
--- a/passbook/saml_idp/migrations/0001_initial.py
+++ b/passbook/providers/saml/migrations/0001_initial.py
@@ -1,5 +1,6 @@
-# Generated by Django 2.1.7 on 2019-02-16 09:13
+# Generated by Django 2.2.6 on 2019-10-07 14:07
+import django.contrib.postgres.fields
import django.db.models.deletion
from django.db import migrations, models
@@ -13,12 +14,27 @@ class Migration(migrations.Migration):
]
operations = [
+ migrations.CreateModel(
+ name='SAMLPropertyMapping',
+ fields=[
+ ('propertymapping_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.PropertyMapping')),
+ ('saml_name', models.TextField()),
+ ('friendly_name', models.TextField(blank=True, default=None, null=True)),
+ ('values', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)),
+ ],
+ options={
+ 'verbose_name': 'SAML Property Mapping',
+ 'verbose_name_plural': 'SAML Property Mappings',
+ },
+ bases=('passbook_core.propertymapping',),
+ ),
migrations.CreateModel(
name='SAMLProvider',
fields=[
('provider_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Provider')),
('name', models.TextField()),
('acs_url', models.URLField()),
+ ('audience', models.TextField(default='')),
('processor_path', models.CharField(max_length=255)),
('issuer', models.TextField()),
('assertion_valid_for', models.IntegerField(default=86400)),
diff --git a/passbook/providers/saml/migrations/__init__.py b/passbook/providers/saml/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/saml_idp/models.py b/passbook/providers/saml/models.py
similarity index 93%
rename from passbook/saml_idp/models.py
rename to passbook/providers/saml/models.py
index 51c730c50..609b5fe6d 100644
--- a/passbook/saml_idp/models.py
+++ b/passbook/providers/saml/models.py
@@ -7,7 +7,7 @@ from structlog import get_logger
from passbook.core.models import PropertyMapping, Provider
from passbook.lib.utils.reflection import class_to_path, path_to_class
-from passbook.saml_idp.base import Processor
+from passbook.providers.saml.base import Processor
LOGGER = get_logger()
@@ -25,7 +25,7 @@ class SAMLProvider(Provider):
signing_cert = models.TextField()
signing_key = models.TextField()
- form = 'passbook.saml_idp.forms.SAMLProviderForm'
+ form = 'passbook.providers.saml.forms.SAMLProviderForm'
_processor = None
def __init__(self, *args, **kwargs):
@@ -68,7 +68,7 @@ class SAMLPropertyMapping(PropertyMapping):
friendly_name = models.TextField(default=None, blank=True, null=True)
values = ArrayField(models.TextField())
- form = 'passbook.saml_idp.forms.SAMLPropertyMappingForm'
+ form = 'passbook.providers.saml.forms.SAMLPropertyMappingForm'
def __str__(self):
return "SAML Property Mapping %s" % self.saml_name
diff --git a/passbook/providers/saml/processors/__init__.py b/passbook/providers/saml/processors/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/saml_idp/processors/generic.py b/passbook/providers/saml/processors/generic.py
similarity index 73%
rename from passbook/saml_idp/processors/generic.py
rename to passbook/providers/saml/processors/generic.py
index 14c060c82..142ce8428 100644
--- a/passbook/saml_idp/processors/generic.py
+++ b/passbook/providers/saml/processors/generic.py
@@ -1,6 +1,6 @@
"""Generic Processor"""
-from passbook.saml_idp.base import Processor
+from passbook.providers.saml.base import Processor
class GenericProcessor(Processor):
diff --git a/passbook/saml_idp/processors/salesforce.py b/passbook/providers/saml/processors/salesforce.py
similarity index 77%
rename from passbook/saml_idp/processors/salesforce.py
rename to passbook/providers/saml/processors/salesforce.py
index 7a86a5301..178773372 100644
--- a/passbook/saml_idp/processors/salesforce.py
+++ b/passbook/providers/saml/processors/salesforce.py
@@ -1,7 +1,7 @@
"""Salesforce Processor"""
-from passbook.saml_idp.base import Processor
-from passbook.saml_idp.xml_render import get_assertion_xml
+from passbook.providers.saml.base import Processor
+from passbook.providers.saml.xml_render import get_assertion_xml
class SalesForceProcessor(Processor):
diff --git a/passbook/providers/saml/settings.py b/passbook/providers/saml/settings.py
new file mode 100644
index 000000000..0d639b84c
--- /dev/null
+++ b/passbook/providers/saml/settings.py
@@ -0,0 +1,6 @@
+"""saml provider settings"""
+
+PASSBOOK_PROVIDERS_SAML_PROCESSORS = [
+ 'passbook.providers.saml.processors.generic',
+ 'passbook.providers.saml.processors.salesforce',
+]
diff --git a/passbook/saml_idp/templates/saml/idp/invalid_user.html b/passbook/providers/saml/templates/saml/idp/invalid_user.html
similarity index 100%
rename from passbook/saml_idp/templates/saml/idp/invalid_user.html
rename to passbook/providers/saml/templates/saml/idp/invalid_user.html
diff --git a/passbook/saml_idp/templates/saml/idp/logged_out.html b/passbook/providers/saml/templates/saml/idp/logged_out.html
similarity index 100%
rename from passbook/saml_idp/templates/saml/idp/logged_out.html
rename to passbook/providers/saml/templates/saml/idp/logged_out.html
diff --git a/passbook/saml_idp/templates/saml/idp/login.html b/passbook/providers/saml/templates/saml/idp/login.html
similarity index 100%
rename from passbook/saml_idp/templates/saml/idp/login.html
rename to passbook/providers/saml/templates/saml/idp/login.html
diff --git a/passbook/saml_idp/templates/saml/idp/settings.html b/passbook/providers/saml/templates/saml/idp/settings.html
similarity index 100%
rename from passbook/saml_idp/templates/saml/idp/settings.html
rename to passbook/providers/saml/templates/saml/idp/settings.html
diff --git a/passbook/saml_idp/templates/saml/xml/assertions/generic.xml b/passbook/providers/saml/templates/saml/xml/assertions/generic.xml
similarity index 100%
rename from passbook/saml_idp/templates/saml/xml/assertions/generic.xml
rename to passbook/providers/saml/templates/saml/xml/assertions/generic.xml
diff --git a/passbook/saml_idp/templates/saml/xml/assertions/google_apps.xml b/passbook/providers/saml/templates/saml/xml/assertions/google_apps.xml
similarity index 100%
rename from passbook/saml_idp/templates/saml/xml/assertions/google_apps.xml
rename to passbook/providers/saml/templates/saml/xml/assertions/google_apps.xml
diff --git a/passbook/saml_idp/templates/saml/xml/assertions/salesforce.xml b/passbook/providers/saml/templates/saml/xml/assertions/salesforce.xml
similarity index 100%
rename from passbook/saml_idp/templates/saml/xml/assertions/salesforce.xml
rename to passbook/providers/saml/templates/saml/xml/assertions/salesforce.xml
diff --git a/passbook/saml_idp/templates/saml/xml/attributes.xml b/passbook/providers/saml/templates/saml/xml/attributes.xml
similarity index 100%
rename from passbook/saml_idp/templates/saml/xml/attributes.xml
rename to passbook/providers/saml/templates/saml/xml/attributes.xml
diff --git a/passbook/saml_idp/templates/saml/xml/metadata.xml b/passbook/providers/saml/templates/saml/xml/metadata.xml
similarity index 100%
rename from passbook/saml_idp/templates/saml/xml/metadata.xml
rename to passbook/providers/saml/templates/saml/xml/metadata.xml
diff --git a/passbook/saml_idp/templates/saml/xml/response.xml b/passbook/providers/saml/templates/saml/xml/response.xml
similarity index 100%
rename from passbook/saml_idp/templates/saml/xml/response.xml
rename to passbook/providers/saml/templates/saml/xml/response.xml
diff --git a/passbook/saml_idp/templates/saml/xml/signature.xml b/passbook/providers/saml/templates/saml/xml/signature.xml
similarity index 100%
rename from passbook/saml_idp/templates/saml/xml/signature.xml
rename to passbook/providers/saml/templates/saml/xml/signature.xml
diff --git a/passbook/saml_idp/templates/saml/xml/subject.xml b/passbook/providers/saml/templates/saml/xml/subject.xml
similarity index 100%
rename from passbook/saml_idp/templates/saml/xml/subject.xml
rename to passbook/providers/saml/templates/saml/xml/subject.xml
diff --git a/passbook/saml_idp/urls.py b/passbook/providers/saml/urls.py
similarity index 94%
rename from passbook/saml_idp/urls.py
rename to passbook/providers/saml/urls.py
index 33a67a66c..4e7d8fdd2 100644
--- a/passbook/saml_idp/urls.py
+++ b/passbook/providers/saml/urls.py
@@ -1,7 +1,7 @@
"""passbook SAML IDP URLs"""
from django.urls import path
-from passbook.saml_idp import views
+from passbook.providers.saml import views
urlpatterns = [
path('/login/',
diff --git a/passbook/saml_idp/utils.py b/passbook/providers/saml/utils.py
similarity index 100%
rename from passbook/saml_idp/utils.py
rename to passbook/providers/saml/utils.py
diff --git a/passbook/saml_idp/views.py b/passbook/providers/saml/views.py
similarity index 98%
rename from passbook/saml_idp/views.py
rename to passbook/providers/saml/views.py
index 5cafc866b..0d76601a0 100644
--- a/passbook/saml_idp/views.py
+++ b/passbook/providers/saml/views.py
@@ -17,9 +17,9 @@ from passbook.audit.models import AuditEntry
from passbook.core.models import Application
from passbook.lib.mixins import CSRFExemptMixin
from passbook.lib.utils.template import render_to_string
-from passbook.policy.engine import PolicyEngine
-from passbook.saml_idp import exceptions
-from passbook.saml_idp.models import SAMLProvider
+from passbook.policies.engine import PolicyEngine
+from passbook.providers.saml import exceptions
+from passbook.providers.saml.models import SAMLProvider
LOGGER = get_logger()
URL_VALIDATOR = URLValidator(schemes=('http', 'https'))
diff --git a/passbook/saml_idp/xml_render.py b/passbook/providers/saml/xml_render.py
similarity index 88%
rename from passbook/saml_idp/xml_render.py
rename to passbook/providers/saml/xml_render.py
index edce64110..460d60cca 100644
--- a/passbook/saml_idp/xml_render.py
+++ b/passbook/providers/saml/xml_render.py
@@ -1,9 +1,15 @@
"""Functions for creating XML output."""
+from __future__ import annotations
+from typing import TYPE_CHECKING
from structlog import get_logger
from passbook.lib.utils.template import render_to_string
-from passbook.saml_idp.xml_signing import get_signature_xml, sign_with_signxml
+from passbook.providers.saml.xml_signing import (get_signature_xml,
+ sign_with_signxml)
+
+if TYPE_CHECKING:
+ from passbook.providers.saml.models import SAMLProvider
LOGGER = get_logger()
@@ -63,7 +69,7 @@ def get_assertion_xml(template, parameters, signed=False):
return render_to_string(template, params)
-def get_response_xml(parameters, saml_provider: 'SAMLProvider', assertion_id=''):
+def get_response_xml(parameters, saml_provider: SAMLProvider, assertion_id=''):
"""Returns XML for response, with signatures, if signed is True."""
# Reset signatures.
params = {}
diff --git a/passbook/saml_idp/xml_signing.py b/passbook/providers/saml/xml_signing.py
similarity index 100%
rename from passbook/saml_idp/xml_signing.py
rename to passbook/providers/saml/xml_signing.py
diff --git a/passbook/root/settings.py b/passbook/root/settings.py
index 073074fa0..a8455198b 100644
--- a/passbook/root/settings.py
+++ b/passbook/root/settings.py
@@ -68,20 +68,30 @@ INSTALLED_APPS = [
'passbook.core.apps.PassbookCoreConfig',
'passbook.admin.apps.PassbookAdminConfig',
'passbook.api.apps.PassbookAPIConfig',
- 'passbook.audit.apps.PassbookAuditConfig',
'passbook.lib.apps.PassbookLibConfig',
- 'passbook.ldap.apps.PassbookLdapConfig',
- 'passbook.oauth_client.apps.PassbookOAuthClientConfig',
- 'passbook.oauth_provider.apps.PassbookOAuthProviderConfig',
- 'passbook.oidc_provider.apps.PassbookOIDCProviderConfig',
- 'passbook.saml_idp.apps.PassbookSAMLIDPConfig',
- 'passbook.otp.apps.PassbookOTPConfig',
- 'passbook.captcha_factor.apps.PassbookCaptchaFactorConfig',
- 'passbook.hibp_policy.apps.PassbookHIBPConfig',
- 'passbook.pretend.apps.PassbookPretendConfig',
- 'passbook.password_expiry_policy.apps.PassbookPasswordExpiryPolicyConfig',
- 'passbook.suspicious_policy.apps.PassbookSuspiciousPolicyConfig',
- 'passbook.app_gw.apps.PassbookApplicationApplicationGatewayConfig',
+ 'passbook.audit.apps.PassbookAuditConfig',
+
+ 'passbook.sources.ldap.apps.PassbookSourceLDAPConfig',
+ 'passbook.sources.oauth.apps.PassbookSourceOAuthConfig',
+
+ 'passbook.providers.app_gw.apps.PassbookApplicationApplicationGatewayConfig',
+ 'passbook.providers.oauth.apps.PassbookProviderOAuthConfig',
+ 'passbook.providers.oidc.apps.PassbookProviderOIDCConfig',
+ 'passbook.providers.saml.apps.PassbookProviderSAMLConfig',
+
+ 'passbook.factors.otp.apps.PassbookFactorOTPConfig',
+ 'passbook.factors.captcha.apps.PassbookFactorCaptchaConfig',
+ 'passbook.factors.password.apps.PassbookFactorPasswordConfig',
+ 'passbook.factors.dummy.apps.PassbookFactorDummyConfig',
+
+ 'passbook.policies.expiry.apps.PassbookPolicyExpiryConfig',
+ 'passbook.policies.reputation.apps.PassbookPolicyReputationConfig',
+ 'passbook.policies.hibp.apps.PassbookPolicyHIBPConfig',
+ 'passbook.policies.group.apps.PassbookPoliciesGroupConfig',
+ 'passbook.policies.matcher.apps.PassbookPoliciesMatcherConfig',
+ 'passbook.policies.password.apps.PassbookPoliciesPasswordConfig',
+ 'passbook.policies.sso.apps.PassbookPoliciesSSOConfig',
+ 'passbook.policies.webhook.apps.PassbookPoliciesWebhookConfig',
]
REST_FRAMEWORK = {
@@ -292,8 +302,7 @@ if any('test' in arg for arg in sys.argv):
_DISALLOWED_ITEMS = ['INSTALLED_APPS', 'MIDDLEWARE', 'AUTHENTICATION_BACKENDS']
# Load subapps's INSTALLED_APPS
for _app in INSTALLED_APPS:
- if _app.startswith('passbook') and \
- not _app.startswith('passbook.core'):
+ if _app.startswith('passbook'):
if 'apps' in _app:
_app = '.'.join(_app.split('.')[:-2])
try:
diff --git a/passbook/saml_idp/migrations/0002_samlpropertymapping.py b/passbook/saml_idp/migrations/0002_samlpropertymapping.py
deleted file mode 100644
index 7fe8de720..000000000
--- a/passbook/saml_idp/migrations/0002_samlpropertymapping.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-08 10:40
-
-import django.contrib.postgres.fields
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_core', '0017_propertymapping'),
- ('passbook_saml_idp', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='SAMLPropertyMapping',
- fields=[
- ('propertymapping_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.PropertyMapping')),
- ('saml_name', models.TextField()),
- ('friendly_name', models.TextField(blank=True, default=None, null=True)),
- ('values', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)),
- ],
- options={
- 'verbose_name': 'SAML Property Mapping',
- 'verbose_name_plural': 'SAML Property Mappings',
- },
- bases=('passbook_core.propertymapping',),
- ),
- ]
diff --git a/passbook/saml_idp/migrations/0003_samlprovider_audience.py b/passbook/saml_idp/migrations/0003_samlprovider_audience.py
deleted file mode 100644
index f183d53a8..000000000
--- a/passbook/saml_idp/migrations/0003_samlprovider_audience.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2019-04-18 09:09
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_saml_idp', '0002_samlpropertymapping'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='samlprovider',
- name='audience',
- field=models.TextField(blank=True, default=''),
- ),
- ]
diff --git a/passbook/saml_idp/migrations/0004_auto_20190418_0918.py b/passbook/saml_idp/migrations/0004_auto_20190418_0918.py
deleted file mode 100644
index a12725415..000000000
--- a/passbook/saml_idp/migrations/0004_auto_20190418_0918.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2019-04-18 09:18
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_saml_idp', '0003_samlprovider_audience'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='samlprovider',
- name='audience',
- field=models.TextField(default=''),
- ),
- ]
diff --git a/passbook/sources/ldap/__init__.py b/passbook/sources/ldap/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/ldap/admin.py b/passbook/sources/ldap/admin.py
similarity index 63%
rename from passbook/ldap/admin.py
rename to passbook/sources/ldap/admin.py
index 09facac6b..8dd111cd0 100644
--- a/passbook/ldap/admin.py
+++ b/passbook/sources/ldap/admin.py
@@ -2,4 +2,4 @@
from passbook.lib.admin import admin_autoregister
-admin_autoregister('passbook_ldap')
+admin_autoregister('passbook_sources_ldap')
diff --git a/passbook/sources/ldap/apps.py b/passbook/sources/ldap/apps.py
new file mode 100644
index 000000000..7a56777dc
--- /dev/null
+++ b/passbook/sources/ldap/apps.py
@@ -0,0 +1,11 @@
+"""Passbook ldap app config"""
+
+from django.apps import AppConfig
+
+
+class PassbookSourceLDAPConfig(AppConfig):
+ """Passbook ldap app config"""
+
+ name = 'passbook.sources.ldap'
+ label = 'passbook_sources_ldap'
+ verbose_name = 'passbook Sources.LDAP'
diff --git a/passbook/ldap/auth.py b/passbook/sources/ldap/auth.py
similarity index 84%
rename from passbook/ldap/auth.py
rename to passbook/sources/ldap/auth.py
index 6c7b59d5a..86508b2b3 100644
--- a/passbook/ldap/auth.py
+++ b/passbook/sources/ldap/auth.py
@@ -2,8 +2,8 @@
from django.contrib.auth.backends import ModelBackend
from structlog import get_logger
-from passbook.ldap.ldap_connector import LDAPConnector
-from passbook.ldap.models import LDAPSource
+from passbook.sources.ldap.ldap_connector import LDAPConnector
+from passbook.sources.ldap.models import LDAPSource
LOGGER = get_logger()
diff --git a/passbook/ldap/forms.py b/passbook/sources/ldap/forms.py
similarity index 94%
rename from passbook/ldap/forms.py
rename to passbook/sources/ldap/forms.py
index c663a1c00..2a2aad74a 100644
--- a/passbook/ldap/forms.py
+++ b/passbook/sources/ldap/forms.py
@@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _
from passbook.admin.forms.source import SOURCE_FORM_FIELDS
from passbook.core.forms.policies import GENERAL_FIELDS
-from passbook.ldap.models import LDAPGroupMembershipPolicy, LDAPSource
+from passbook.sources.ldap.models import LDAPGroupMembershipPolicy, LDAPSource
class LDAPSourceForm(forms.ModelForm):
diff --git a/passbook/ldap/ldap_connector.py b/passbook/sources/ldap/ldap_connector.py
similarity index 99%
rename from passbook/ldap/ldap_connector.py
rename to passbook/sources/ldap/ldap_connector.py
index 29e9efb75..925652c83 100644
--- a/passbook/ldap/ldap_connector.py
+++ b/passbook/sources/ldap/ldap_connector.py
@@ -6,8 +6,8 @@ import ldap3.core.exceptions
from structlog import get_logger
from passbook.core.models import User
-from passbook.ldap.models import LDAPSource
from passbook.lib.config import CONFIG
+from passbook.sources.ldap.models import LDAPSource
LOGGER = get_logger()
diff --git a/passbook/ldap/migrations/0001_initial.py b/passbook/sources/ldap/migrations/0001_initial.py
similarity index 61%
rename from passbook/ldap/migrations/0001_initial.py
rename to passbook/sources/ldap/migrations/0001_initial.py
index 4d432c79a..0226b33da 100644
--- a/passbook/ldap/migrations/0001_initial.py
+++ b/passbook/sources/ldap/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-02-16 09:13
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
@@ -32,4 +32,17 @@ class Migration(migrations.Migration):
},
bases=('passbook_core.source',),
),
+ migrations.CreateModel(
+ name='LDAPGroupMembershipPolicy',
+ 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')),
+ ('dn', models.TextField()),
+ ('source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='passbook_sources_ldap.LDAPSource')),
+ ],
+ options={
+ 'verbose_name': 'LDAP Group Membership Policy',
+ 'verbose_name_plural': 'LDAP Group Membership Policys',
+ },
+ bases=('passbook_core.policy',),
+ ),
]
diff --git a/passbook/sources/ldap/migrations/__init__.py b/passbook/sources/ldap/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/ldap/models.py b/passbook/sources/ldap/models.py
similarity index 91%
rename from passbook/ldap/models.py
rename to passbook/sources/ldap/models.py
index 190da2ebe..a7c6f0b5b 100644
--- a/passbook/ldap/models.py
+++ b/passbook/sources/ldap/models.py
@@ -26,7 +26,7 @@ class LDAPSource(Source):
create_user = models.BooleanField(default=False)
reset_password = models.BooleanField(default=True)
- form = 'passbook.ldap.forms.LDAPSourceForm'
+ form = 'passbook.sources.ldap.forms.LDAPSourceForm'
@property
def get_login_button(self):
@@ -43,7 +43,7 @@ class LDAPGroupMembershipPolicy(Policy):
dn = models.TextField()
source = models.ForeignKey('LDAPSource', on_delete=models.CASCADE)
- form = 'passbook.ldap.forms.LDAPGroupMembershipPolicyForm'
+ form = 'passbook.sources.ldap.forms.LDAPGroupMembershipPolicyForm'
def passes(self, user: User):
"""Check if user instance passes this policy"""
diff --git a/passbook/ldap/settings.py b/passbook/sources/ldap/settings.py
similarity index 52%
rename from passbook/ldap/settings.py
rename to passbook/sources/ldap/settings.py
index 500815b02..cb69c276b 100644
--- a/passbook/ldap/settings.py
+++ b/passbook/sources/ldap/settings.py
@@ -1,5 +1,5 @@
"""LDAP Settings"""
AUTHENTICATION_BACKENDS = [
- 'passbook.ldap.auth.LDAPBackend',
+ 'passbook.sources.ldap.auth.LDAPBackend',
]
diff --git a/passbook/ldap/templates/ldap/settings.html b/passbook/sources/ldap/templates/ldap/settings.html
similarity index 100%
rename from passbook/ldap/templates/ldap/settings.html
rename to passbook/sources/ldap/templates/ldap/settings.html
diff --git a/passbook/ldap/urls.py b/passbook/sources/ldap/urls.py
similarity index 100%
rename from passbook/ldap/urls.py
rename to passbook/sources/ldap/urls.py
diff --git a/passbook/ldap/views.py b/passbook/sources/ldap/views.py
similarity index 95%
rename from passbook/ldap/views.py
rename to passbook/sources/ldap/views.py
index 5eb73d82e..7e3e2c6a5 100644
--- a/passbook/ldap/views.py
+++ b/passbook/sources/ldap/views.py
@@ -8,7 +8,7 @@
# from django.urls import reverse
# from django.utils.translation import ugettext as _
-# from passbook.ldap.forms import (AuthenticationBackendSettings,
+# from passbook.sources.ldap.forms import (AuthenticationBackendSettings,
# ConnectionSettings,
# CreateUsersSettings,
# GeneralSettingsForm)
diff --git a/passbook/sources/oauth/__init__.py b/passbook/sources/oauth/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/oauth_client/admin.py b/passbook/sources/oauth/admin.py
similarity index 65%
rename from passbook/oauth_client/admin.py
rename to passbook/sources/oauth/admin.py
index a2f52168b..50cec0a49 100644
--- a/passbook/oauth_client/admin.py
+++ b/passbook/sources/oauth/admin.py
@@ -2,4 +2,4 @@
from passbook.lib.admin import admin_autoregister
-admin_autoregister('passbook_oauth_client')
+admin_autoregister('passbook_sources_oauth')
diff --git a/passbook/oauth_client/apps.py b/passbook/sources/oauth/apps.py
similarity index 57%
rename from passbook/oauth_client/apps.py
rename to passbook/sources/oauth/apps.py
index a8e12e668..1672ed344 100644
--- a/passbook/oauth_client/apps.py
+++ b/passbook/sources/oauth/apps.py
@@ -2,24 +2,22 @@
from importlib import import_module
from django.apps import AppConfig
+from django.conf import settings
from structlog import get_logger
-from passbook.lib.config import CONFIG
-
LOGGER = get_logger()
-class PassbookOAuthClientConfig(AppConfig):
- """passbook oauth_client config"""
+class PassbookSourceOAuthConfig(AppConfig):
+ """passbook source.oauth config"""
- name = 'passbook.oauth_client'
- label = 'passbook_oauth_client'
- verbose_name = 'passbook OAuth Client'
+ name = 'passbook.sources.oauth'
+ label = 'passbook_sources_oauth'
+ verbose_name = 'passbook Sources.OAuth'
mountpoint = 'source/oauth/'
def ready(self):
"""Load source_types from config file"""
- source_types_to_load = CONFIG.y('oauth_client.types', [])
- for source_type in source_types_to_load:
+ for source_type in settings.PASSBOOK_SOURCES_OAUTH_TYPES:
try:
import_module(source_type)
LOGGER.info("Loaded source_type", source_class=source_type)
diff --git a/passbook/oauth_client/backends.py b/passbook/sources/oauth/backends.py
similarity index 85%
rename from passbook/oauth_client/backends.py
rename to passbook/sources/oauth/backends.py
index 647fc048f..055aa8a69 100644
--- a/passbook/oauth_client/backends.py
+++ b/passbook/sources/oauth/backends.py
@@ -3,7 +3,8 @@
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
-from passbook.oauth_client.models import OAuthSource, UserOAuthSourceConnection
+from passbook.sources.oauth.models import (OAuthSource,
+ UserOAuthSourceConnection)
class AuthorizedServiceBackend(ModelBackend):
diff --git a/passbook/oauth_client/clients.py b/passbook/sources/oauth/clients.py
similarity index 100%
rename from passbook/oauth_client/clients.py
rename to passbook/sources/oauth/clients.py
diff --git a/passbook/oauth_client/forms.py b/passbook/sources/oauth/forms.py
similarity index 97%
rename from passbook/oauth_client/forms.py
rename to passbook/sources/oauth/forms.py
index a4e49ff00..8c6fb78d9 100644
--- a/passbook/oauth_client/forms.py
+++ b/passbook/sources/oauth/forms.py
@@ -5,8 +5,8 @@ from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext as _
from passbook.admin.forms.source import SOURCE_FORM_FIELDS
-from passbook.oauth_client.models import OAuthSource
-from passbook.oauth_client.source_types.manager import MANAGER
+from passbook.sources.oauth.models import OAuthSource
+from passbook.sources.oauth.types.manager import MANAGER
class OAuthSourceForm(forms.ModelForm):
diff --git a/passbook/oauth_client/locale/de/LC_MESSAGES/django.po b/passbook/sources/oauth/locale/de/LC_MESSAGES/django.po
similarity index 100%
rename from passbook/oauth_client/locale/de/LC_MESSAGES/django.po
rename to passbook/sources/oauth/locale/de/LC_MESSAGES/django.po
diff --git a/passbook/oauth_client/locale/en/LC_MESSAGES/django.po b/passbook/sources/oauth/locale/en/LC_MESSAGES/django.po
similarity index 100%
rename from passbook/oauth_client/locale/en/LC_MESSAGES/django.po
rename to passbook/sources/oauth/locale/en/LC_MESSAGES/django.po
diff --git a/passbook/oauth_client/locale/es/LC_MESSAGES/django.po b/passbook/sources/oauth/locale/es/LC_MESSAGES/django.po
similarity index 100%
rename from passbook/oauth_client/locale/es/LC_MESSAGES/django.po
rename to passbook/sources/oauth/locale/es/LC_MESSAGES/django.po
diff --git a/passbook/oauth_client/locale/fr/LC_MESSAGES/django.po b/passbook/sources/oauth/locale/fr/LC_MESSAGES/django.po
similarity index 100%
rename from passbook/oauth_client/locale/fr/LC_MESSAGES/django.po
rename to passbook/sources/oauth/locale/fr/LC_MESSAGES/django.po
diff --git a/passbook/oauth_client/migrations/0001_initial.py b/passbook/sources/oauth/migrations/0001_initial.py
similarity index 97%
rename from passbook/oauth_client/migrations/0001_initial.py
rename to passbook/sources/oauth/migrations/0001_initial.py
index 5642bec33..6e61b2815 100644
--- a/passbook/oauth_client/migrations/0001_initial.py
+++ b/passbook/sources/oauth/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-02-16 09:13
+# Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion
from django.db import migrations, models
diff --git a/passbook/sources/oauth/migrations/__init__.py b/passbook/sources/oauth/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/oauth_client/models.py b/passbook/sources/oauth/models.py
similarity index 84%
rename from passbook/oauth_client/models.py
rename to passbook/sources/oauth/models.py
index 99f37db42..053889194 100644
--- a/passbook/oauth_client/models.py
+++ b/passbook/sources/oauth/models.py
@@ -5,7 +5,7 @@ from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _
from passbook.core.models import Source, UserSourceConnection
-from passbook.oauth_client.clients import get_client
+from passbook.sources.oauth.clients import get_client
class OAuthSource(Source):
@@ -19,7 +19,7 @@ class OAuthSource(Source):
consumer_key = models.TextField()
consumer_secret = models.TextField()
- form = 'passbook.oauth_client.forms.OAuthSourceForm'
+ form = 'passbook.sources.oauth.forms.OAuthSourceForm'
@property
def is_link(self):
@@ -27,14 +27,14 @@ class OAuthSource(Source):
@property
def get_login_button(self):
- url = reverse_lazy('passbook_oauth_client:oauth-client-login',
+ url = reverse_lazy('passbook_sources_oauth:oauth-client-login',
kwargs={'source_slug': self.slug})
return url, self.provider_type, self.name
@property
def additional_info(self):
return "Callback URL: %s
" % \
- reverse_lazy('passbook_oauth_client:oauth-client-callback',
+ reverse_lazy('passbook_sources_oauth:oauth-client-callback',
kwargs={'source_slug': self.slug})
def has_user_settings(self):
@@ -45,7 +45,7 @@ class OAuthSource(Source):
if icon_type == 'azure ad':
icon_type = 'windows'
icon_class = 'fa fa-%s' % icon_type
- view_name = 'passbook_oauth_client:oauth-client-user'
+ view_name = 'passbook_sources_oauth:oauth-client-user'
return self.name, icon_class, reverse((view_name), kwargs={
'source_slug': self.slug
})
@@ -59,7 +59,7 @@ class OAuthSource(Source):
class GitHubOAuthSource(OAuthSource):
"""Abstract subclass of OAuthSource to specify GitHub Form"""
- form = 'passbook.oauth_client.forms.GitHubOAuthSourceForm'
+ form = 'passbook.sources.oauth.forms.GitHubOAuthSourceForm'
class Meta:
@@ -71,7 +71,7 @@ class GitHubOAuthSource(OAuthSource):
class TwitterOAuthSource(OAuthSource):
"""Abstract subclass of OAuthSource to specify Twitter Form"""
- form = 'passbook.oauth_client.forms.TwitterOAuthSourceForm'
+ form = 'passbook.sources.oauth.forms.TwitterOAuthSourceForm'
class Meta:
@@ -83,7 +83,7 @@ class TwitterOAuthSource(OAuthSource):
class FacebookOAuthSource(OAuthSource):
"""Abstract subclass of OAuthSource to specify Facebook Form"""
- form = 'passbook.oauth_client.forms.FacebookOAuthSourceForm'
+ form = 'passbook.sources.oauth.forms.FacebookOAuthSourceForm'
class Meta:
@@ -95,7 +95,7 @@ class FacebookOAuthSource(OAuthSource):
class DiscordOAuthSource(OAuthSource):
"""Abstract subclass of OAuthSource to specify Discord Form"""
- form = 'passbook.oauth_client.forms.DiscordOAuthSourceForm'
+ form = 'passbook.sources.oauth.forms.DiscordOAuthSourceForm'
class Meta:
@@ -107,7 +107,7 @@ class DiscordOAuthSource(OAuthSource):
class GoogleOAuthSource(OAuthSource):
"""Abstract subclass of OAuthSource to specify Google Form"""
- form = 'passbook.oauth_client.forms.GoogleOAuthSourceForm'
+ form = 'passbook.sources.oauth.forms.GoogleOAuthSourceForm'
class Meta:
@@ -119,7 +119,7 @@ class GoogleOAuthSource(OAuthSource):
class AzureADOAuthSource(OAuthSource):
"""Abstract subclass of OAuthSource to specify AzureAD Form"""
- form = 'passbook.oauth_client.forms.AzureADOAuthSourceForm'
+ form = 'passbook.sources.oauth.forms.AzureADOAuthSourceForm'
class Meta:
diff --git a/passbook/sources/oauth/settings.py b/passbook/sources/oauth/settings.py
new file mode 100644
index 000000000..2158129e0
--- /dev/null
+++ b/passbook/sources/oauth/settings.py
@@ -0,0 +1,16 @@
+"""Oauth2 Client Settings"""
+
+AUTHENTICATION_BACKENDS = [
+ 'passbook.sources.oauth.backends.AuthorizedServiceBackend',
+]
+
+PASSBOOK_SOURCES_OAUTH_TYPES = [
+ 'passbook.sources.oauth.types.discord',
+ 'passbook.sources.oauth.types.facebook',
+ 'passbook.sources.oauth.types.github',
+ 'passbook.sources.oauth.types.google',
+ 'passbook.sources.oauth.types.reddit',
+ 'passbook.sources.oauth.types.supervisr',
+ 'passbook.sources.oauth.types.twitter',
+ 'passbook.sources.oauth.types.azure_ad',
+]
diff --git a/passbook/oauth_client/templates/oauth_client/user.html b/passbook/sources/oauth/templates/oauth_client/user.html
similarity index 100%
rename from passbook/oauth_client/templates/oauth_client/user.html
rename to passbook/sources/oauth/templates/oauth_client/user.html
diff --git a/passbook/sources/oauth/types/__init__.py b/passbook/sources/oauth/types/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/oauth_client/source_types/azure_ad.py b/passbook/sources/oauth/types/azure_ad.py
similarity index 85%
rename from passbook/oauth_client/source_types/azure_ad.py
rename to passbook/sources/oauth/types/azure_ad.py
index b35c6b163..f9d2cdafe 100644
--- a/passbook/oauth_client/source_types/azure_ad.py
+++ b/passbook/sources/oauth/types/azure_ad.py
@@ -5,10 +5,10 @@ import uuid
from requests.exceptions import RequestException
from structlog import get_logger
-from passbook.oauth_client.clients import OAuth2Client
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
-from passbook.oauth_client.utils import user_get_or_create
-from passbook.oauth_client.views.core import OAuthCallback
+from passbook.sources.oauth.clients import OAuth2Client
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.utils import user_get_or_create
+from passbook.sources.oauth.views.core import OAuthCallback
LOGGER = get_logger()
diff --git a/passbook/oauth_client/source_types/discord.py b/passbook/sources/oauth/types/discord.py
similarity index 86%
rename from passbook/oauth_client/source_types/discord.py
rename to passbook/sources/oauth/types/discord.py
index c8c9e714e..9361d44c0 100644
--- a/passbook/oauth_client/source_types/discord.py
+++ b/passbook/sources/oauth/types/discord.py
@@ -4,10 +4,10 @@ import json
from requests.exceptions import RequestException
from structlog import get_logger
-from passbook.oauth_client.clients import OAuth2Client
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
-from passbook.oauth_client.utils import user_get_or_create
-from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
+from passbook.sources.oauth.clients import OAuth2Client
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.utils import user_get_or_create
+from passbook.sources.oauth.views.core import OAuthCallback, OAuthRedirect
LOGGER = get_logger()
diff --git a/passbook/oauth_client/source_types/facebook.py b/passbook/sources/oauth/types/facebook.py
similarity index 77%
rename from passbook/oauth_client/source_types/facebook.py
rename to passbook/sources/oauth/types/facebook.py
index 00b387177..0f6fd3a9c 100644
--- a/passbook/oauth_client/source_types/facebook.py
+++ b/passbook/sources/oauth/types/facebook.py
@@ -1,8 +1,8 @@
"""Facebook OAuth Views"""
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
-from passbook.oauth_client.utils import user_get_or_create
-from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.utils import user_get_or_create
+from passbook.sources.oauth.views.core import OAuthCallback, OAuthRedirect
@MANAGER.source(kind=RequestKind.redirect, name='Facebook')
diff --git a/passbook/oauth_client/source_types/github.py b/passbook/sources/oauth/types/github.py
similarity index 71%
rename from passbook/oauth_client/source_types/github.py
rename to passbook/sources/oauth/types/github.py
index cfdcc3a7c..bfa0580dc 100644
--- a/passbook/oauth_client/source_types/github.py
+++ b/passbook/sources/oauth/types/github.py
@@ -1,8 +1,8 @@
"""GitHub OAuth Views"""
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
-from passbook.oauth_client.utils import user_get_or_create
-from passbook.oauth_client.views.core import OAuthCallback
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.utils import user_get_or_create
+from passbook.sources.oauth.views.core import OAuthCallback
@MANAGER.source(kind=RequestKind.callback, name='GitHub')
diff --git a/passbook/oauth_client/source_types/google.py b/passbook/sources/oauth/types/google.py
similarity index 77%
rename from passbook/oauth_client/source_types/google.py
rename to passbook/sources/oauth/types/google.py
index 7cc49c74f..b244359b3 100644
--- a/passbook/oauth_client/source_types/google.py
+++ b/passbook/sources/oauth/types/google.py
@@ -1,7 +1,7 @@
"""Google OAuth Views"""
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
-from passbook.oauth_client.utils import user_get_or_create
-from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.utils import user_get_or_create
+from passbook.sources.oauth.views.core import OAuthCallback, OAuthRedirect
@MANAGER.source(kind=RequestKind.redirect, name='Google')
diff --git a/passbook/oauth_client/source_types/manager.py b/passbook/sources/oauth/types/manager.py
similarity index 95%
rename from passbook/oauth_client/source_types/manager.py
rename to passbook/sources/oauth/types/manager.py
index 389f12c0f..be4d366f7 100644
--- a/passbook/oauth_client/source_types/manager.py
+++ b/passbook/sources/oauth/types/manager.py
@@ -3,7 +3,7 @@ from enum import Enum
from structlog import get_logger
-from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
+from passbook.sources.oauth.views.core import OAuthCallback, OAuthRedirect
LOGGER = get_logger()
diff --git a/passbook/oauth_client/source_types/reddit.py b/passbook/sources/oauth/types/reddit.py
similarity index 88%
rename from passbook/oauth_client/source_types/reddit.py
rename to passbook/sources/oauth/types/reddit.py
index b2dd3a71b..0b349bbf7 100644
--- a/passbook/oauth_client/source_types/reddit.py
+++ b/passbook/sources/oauth/types/reddit.py
@@ -5,10 +5,10 @@ from requests.auth import HTTPBasicAuth
from requests.exceptions import RequestException
from structlog import get_logger
-from passbook.oauth_client.clients import OAuth2Client
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
-from passbook.oauth_client.utils import user_get_or_create
-from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
+from passbook.sources.oauth.clients import OAuth2Client
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.utils import user_get_or_create
+from passbook.sources.oauth.views.core import OAuthCallback, OAuthRedirect
LOGGER = get_logger()
diff --git a/passbook/oauth_client/source_types/supervisr.py b/passbook/sources/oauth/types/supervisr.py
similarity index 85%
rename from passbook/oauth_client/source_types/supervisr.py
rename to passbook/sources/oauth/types/supervisr.py
index 4a30cba7f..7f3670b1e 100644
--- a/passbook/oauth_client/source_types/supervisr.py
+++ b/passbook/sources/oauth/types/supervisr.py
@@ -5,10 +5,10 @@ import json
from requests.exceptions import RequestException
from structlog import get_logger
-from passbook.oauth_client.clients import OAuth2Client
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
-from passbook.oauth_client.utils import user_get_or_create
-from passbook.oauth_client.views.core import OAuthCallback
+from passbook.sources.oauth.clients import OAuth2Client
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.utils import user_get_or_create
+from passbook.sources.oauth.views.core import OAuthCallback
LOGGER = get_logger()
diff --git a/passbook/oauth_client/source_types/twitter.py b/passbook/sources/oauth/types/twitter.py
similarity index 82%
rename from passbook/oauth_client/source_types/twitter.py
rename to passbook/sources/oauth/types/twitter.py
index fb49f9d4d..b43a0cfc5 100644
--- a/passbook/oauth_client/source_types/twitter.py
+++ b/passbook/sources/oauth/types/twitter.py
@@ -3,10 +3,10 @@
from requests.exceptions import RequestException
from structlog import get_logger
-from passbook.oauth_client.clients import OAuthClient
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
-from passbook.oauth_client.utils import user_get_or_create
-from passbook.oauth_client.views.core import OAuthCallback
+from passbook.sources.oauth.clients import OAuthClient
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.utils import user_get_or_create
+from passbook.sources.oauth.views.core import OAuthCallback
LOGGER = get_logger()
diff --git a/passbook/oauth_client/urls.py b/passbook/sources/oauth/urls.py
similarity index 81%
rename from passbook/oauth_client/urls.py
rename to passbook/sources/oauth/urls.py
index e633f0a3a..b15f27314 100644
--- a/passbook/oauth_client/urls.py
+++ b/passbook/sources/oauth/urls.py
@@ -2,8 +2,8 @@
from django.urls import path
-from passbook.oauth_client.source_types.manager import RequestKind
-from passbook.oauth_client.views import core, dispatcher, user
+from passbook.sources.oauth.types.manager import RequestKind
+from passbook.sources.oauth.views import core, dispatcher, user
urlpatterns = [
path('login//', dispatcher.DispatcherView.as_view(
diff --git a/passbook/oauth_client/utils.py b/passbook/sources/oauth/utils.py
similarity index 100%
rename from passbook/oauth_client/utils.py
rename to passbook/sources/oauth/utils.py
diff --git a/passbook/sources/oauth/views/__init__.py b/passbook/sources/oauth/views/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/oauth_client/views/core.py b/passbook/sources/oauth/views/core.py
similarity index 97%
rename from passbook/oauth_client/views/core.py
rename to passbook/sources/oauth/views/core.py
index b137fec8f..da47a96de 100644
--- a/passbook/oauth_client/views/core.py
+++ b/passbook/sources/oauth/views/core.py
@@ -11,10 +11,11 @@ from django.utils.translation import ugettext as _
from django.views.generic import RedirectView, View
from structlog import get_logger
-from passbook.core.auth.view import AuthenticationView, _redirect_with_qs
+from passbook.factors.view import AuthenticationView, _redirect_with_qs
from passbook.lib.utils.reflection import app
-from passbook.oauth_client.clients import get_client
-from passbook.oauth_client.models import OAuthSource, UserOAuthSourceConnection
+from passbook.sources.oauth.clients import get_client
+from passbook.sources.oauth.models import (OAuthSource,
+ UserOAuthSourceConnection)
LOGGER = get_logger()
diff --git a/passbook/oauth_client/views/dispatcher.py b/passbook/sources/oauth/views/dispatcher.py
similarity index 83%
rename from passbook/oauth_client/views/dispatcher.py
rename to passbook/sources/oauth/views/dispatcher.py
index 6d682a422..6b25b8f9c 100644
--- a/passbook/oauth_client/views/dispatcher.py
+++ b/passbook/sources/oauth/views/dispatcher.py
@@ -3,8 +3,8 @@ from django.http import Http404
from django.shortcuts import get_object_or_404
from django.views import View
-from passbook.oauth_client.models import OAuthSource
-from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
+from passbook.sources.oauth.models import OAuthSource
+from passbook.sources.oauth.types.manager import MANAGER, RequestKind
class DispatcherView(View):
diff --git a/passbook/oauth_client/views/user.py b/passbook/sources/oauth/views/user.py
similarity index 85%
rename from passbook/oauth_client/views/user.py
rename to passbook/sources/oauth/views/user.py
index 0fd3378e5..c47a670c3 100644
--- a/passbook/oauth_client/views/user.py
+++ b/passbook/sources/oauth/views/user.py
@@ -3,7 +3,8 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.views.generic import TemplateView
-from passbook.oauth_client.models import OAuthSource, UserOAuthSourceConnection
+from passbook.sources.oauth.models import (OAuthSource,
+ UserOAuthSourceConnection)
class UserSettingsView(LoginRequiredMixin, TemplateView):
diff --git a/passbook/suspicious_policy/admin.py b/passbook/suspicious_policy/admin.py
deleted file mode 100644
index 983385432..000000000
--- a/passbook/suspicious_policy/admin.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""Passbook suspicious_policy Admin"""
-
-from passbook.lib.admin import admin_autoregister
-
-admin_autoregister('passbook_suspicious_policy')
diff --git a/passbook/suspicious_policy/apps.py b/passbook/suspicious_policy/apps.py
deleted file mode 100644
index c0fb6f6bf..000000000
--- a/passbook/suspicious_policy/apps.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""Passbook suspicious_policy app config"""
-from importlib import import_module
-
-from django.apps import AppConfig
-
-
-class PassbookSuspiciousPolicyConfig(AppConfig):
- """Passbook suspicious_policy app config"""
-
- name = 'passbook.suspicious_policy'
- label = 'passbook_suspicious_policy'
- verbose_name = 'passbook Suspicious Request Detector'
-
- def ready(self):
- import_module('passbook.suspicious_policy.signals')
diff --git a/passbook/suspicious_policy/migrations/0002_auto_20190303_1820.py b/passbook/suspicious_policy/migrations/0002_auto_20190303_1820.py
deleted file mode 100644
index 7db3e4d1c..000000000
--- a/passbook/suspicious_policy/migrations/0002_auto_20190303_1820.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-03 18:20
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_suspicious_policy', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='suspiciousrequestpolicy',
- options={'verbose_name': 'Suspicious Request Policy', 'verbose_name_plural': 'Suspicious Request Policies'},
- ),
- ]
diff --git a/passbook/suspicious_policy/migrations/0003_auto_20190303_1833.py b/passbook/suspicious_policy/migrations/0003_auto_20190303_1833.py
deleted file mode 100644
index 23d2fac40..000000000
--- a/passbook/suspicious_policy/migrations/0003_auto_20190303_1833.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.1.7 on 2019-03-03 18:33
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('passbook_suspicious_policy', '0002_auto_20190303_1820'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='ipscore',
- name='ip',
- field=models.GenericIPAddressField(unique=True),
- ),
- migrations.AlterField(
- model_name='userscore',
- name='user',
- field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ]