Revert "*: providers and sources -> channels, PolicyModel to PolicyBindingModel that uses custom M2M through"

This reverts commit 7ed3ceb960.
This commit is contained in:
Jens Langhammer 2020-05-16 16:02:42 +02:00
parent 7ed3ceb960
commit 406f69080b
293 changed files with 4692 additions and 3244 deletions

View File

@ -1,4 +0,0 @@
"""passbook core inlet form fields"""
INLET_FORM_FIELDS = ["name", "slug", "enabled"]
INLET_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled"]

View File

@ -0,0 +1,4 @@
"""passbook core source form fields"""
SOURCE_FORM_FIELDS = ["name", "slug", "enabled"]
SOURCE_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled"]

View File

@ -8,12 +8,12 @@ from passbook.admin.views import (
debug, debug,
flows, flows,
groups, groups,
inlets,
invitations, invitations,
outlets,
overview, overview,
policies, policy,
property_mapping, property_mapping,
providers,
sources,
stages, stages,
users, users,
) )
@ -39,49 +39,51 @@ urlpatterns = [
applications.ApplicationDeleteView.as_view(), applications.ApplicationDeleteView.as_view(),
name="application-delete", name="application-delete",
), ),
# Inlets # Sources
path("inlets/", inlets.InletListView.as_view(), name="inlets"), path("sources/", sources.SourceListView.as_view(), name="sources"),
path("inlets/create/", inlets.InletCreateView.as_view(), name="inlet-create"), path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
path( path(
"inlets/<uuid:pk>/update/", "sources/<uuid:pk>/update/",
inlets.InletUpdateView.as_view(), sources.SourceUpdateView.as_view(),
name="inlet-update", name="source-update",
), ),
path( path(
"inlets/<uuid:pk>/delete/", "sources/<uuid:pk>/delete/",
inlets.InletDeleteView.as_view(), sources.SourceDeleteView.as_view(),
name="inlet-delete", name="source-delete",
), ),
# Policies # Policies
path("policies/", policies.PolicyListView.as_view(), name="policies"), path("policies/", policy.PolicyListView.as_view(), name="policies"),
path("policies/create/", policies.PolicyCreateView.as_view(), name="policy-create"), path("policies/create/", policy.PolicyCreateView.as_view(), name="policy-create"),
path( path(
"policies/<uuid:pk>/update/", "policies/<uuid:pk>/update/",
policies.PolicyUpdateView.as_view(), policy.PolicyUpdateView.as_view(),
name="policy-update", name="policy-update",
), ),
path( path(
"policies/<uuid:pk>/delete/", "policies/<uuid:pk>/delete/",
policies.PolicyDeleteView.as_view(), policy.PolicyDeleteView.as_view(),
name="policy-delete", name="policy-delete",
), ),
path( path(
"policies/<uuid:pk>/test/", "policies/<uuid:pk>/test/", policy.PolicyTestView.as_view(), name="policy-test"
policies.PolicyTestView.as_view(),
name="policy-test",
), ),
# Outlets # Providers
path("outlets/", outlets.OutletListView.as_view(), name="outlets"), path("providers/", providers.ProviderListView.as_view(), name="providers"),
path("outlets/create/", outlets.OutletCreateView.as_view(), name="outlet-create",),
path( path(
"outlets/<int:pk>/update/", "providers/create/",
outlets.OutletUpdateView.as_view(), providers.ProviderCreateView.as_view(),
name="outlet-update", name="provider-create",
), ),
path( path(
"outlets/<int:pk>/delete/", "providers/<int:pk>/update/",
outlets.OutletDeleteView.as_view(), providers.ProviderUpdateView.as_view(),
name="outlet-delete", name="provider-update",
),
path(
"providers/<int:pk>/delete/",
providers.ProviderDeleteView.as_view(),
name="provider-delete",
), ),
# Stages # Stages
path("stages/", stages.StageListView.as_view(), name="stages"), path("stages/", stages.StageListView.as_view(), name="stages"),

View File

@ -5,9 +5,8 @@ from django.views.generic import TemplateView
from passbook import __version__ from passbook import __version__
from passbook.admin.mixins import AdminRequiredMixin from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import Application, Inlet, Outlet, User from passbook.core.models import Application, Policy, Provider, Source, User
from passbook.flows.models import Flow, Stage from passbook.flows.models import Flow, Stage
from passbook.policies.models import Policy
from passbook.root.celery import CELERY_APP from passbook.root.celery import CELERY_APP
from passbook.stages.invitation.models import Invitation from passbook.stages.invitation.models import Invitation
@ -28,14 +27,16 @@ class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
kwargs["application_count"] = len(Application.objects.all()) kwargs["application_count"] = len(Application.objects.all())
kwargs["policy_count"] = len(Policy.objects.all()) kwargs["policy_count"] = len(Policy.objects.all())
kwargs["user_count"] = len(User.objects.all()) kwargs["user_count"] = len(User.objects.all())
kwargs["outlet_count"] = len(Outlet.objects.all()) kwargs["provider_count"] = len(Provider.objects.all())
kwargs["inlet_count"] = len(Inlet.objects.all()) kwargs["source_count"] = len(Source.objects.all())
kwargs["stage_count"] = len(Stage.objects.all()) kwargs["stage_count"] = len(Stage.objects.all())
kwargs["flow_count"] = len(Flow.objects.all()) kwargs["flow_count"] = len(Flow.objects.all())
kwargs["invitation_count"] = len(Invitation.objects.all()) kwargs["invitation_count"] = len(Invitation.objects.all())
kwargs["version"] = __version__ kwargs["version"] = __version__
kwargs["worker_count"] = len(CELERY_APP.control.ping(timeout=0.5)) kwargs["worker_count"] = len(CELERY_APP.control.ping(timeout=0.5))
kwargs["outlets_without_application"] = Outlet.objects.filter(application=None) kwargs["providers_without_application"] = Provider.objects.filter(
application=None
)
kwargs["policies_without_binding"] = len( kwargs["policies_without_binding"] = len(
Policy.objects.filter(policymodel__isnull=True) Policy.objects.filter(policymodel__isnull=True)
) )

View File

@ -13,10 +13,10 @@ from django.views.generic.detail import DetailView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.forms.policies import PolicyTestForm from passbook.admin.forms.policies import PolicyTestForm
from passbook.core.models import Policy
from passbook.lib.utils.reflection import all_subclasses, path_to_class from passbook.lib.utils.reflection import all_subclasses, path_to_class
from passbook.lib.views import CreateAssignPermView from passbook.lib.views import CreateAssignPermView
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
from passbook.policies.models import Policy
class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView): class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView):

View File

@ -1,4 +1,4 @@
"""passbook Inlet administration""" """passbook Provider administration"""
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import ( from django.contrib.auth.mixins import (
@ -11,23 +11,23 @@ from django.utils.translation import ugettext as _
from django.views.generic import DeleteView, ListView, UpdateView from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.core.models import Inlet from passbook.core.models import Provider
from passbook.lib.utils.reflection import all_subclasses, path_to_class from passbook.lib.utils.reflection import all_subclasses, path_to_class
from passbook.lib.views import CreateAssignPermView from passbook.lib.views import CreateAssignPermView
class InletListView(LoginRequiredMixin, PermissionListMixin, ListView): class ProviderListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all inlets""" """Show list of all providers"""
model = Inlet model = Provider
permission_required = "passbook_core.view_inlet" permission_required = "passbook_core.add_provider"
ordering = "name" template_name = "administration/provider/list.html"
paginate_by = 40 paginate_by = 10
template_name = "administration/inlet/list.html" ordering = "id"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs["types"] = { kwargs["types"] = {
x.__name__: x._meta.verbose_name for x in all_subclasses(Inlet) x.__name__: x._meta.verbose_name for x in all_subclasses(Provider)
} }
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
@ -35,40 +35,40 @@ class InletListView(LoginRequiredMixin, PermissionListMixin, ListView):
return super().get_queryset().select_subclasses() return super().get_queryset().select_subclasses()
class InletCreateView( class ProviderCreateView(
SuccessMessageMixin, SuccessMessageMixin,
LoginRequiredMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, DjangoPermissionRequiredMixin,
CreateAssignPermView, CreateAssignPermView,
): ):
"""Create new Inlet""" """Create new Provider"""
model = Inlet model = Provider
permission_required = "passbook_core.add_inlet" permission_required = "passbook_core.add_provider"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("passbook_admin:inlets") success_url = reverse_lazy("passbook_admin:providers")
success_message = _("Successfully created Inlet") success_message = _("Successfully created Provider")
def get_form_class(self): def get_form_class(self):
inlet_type = self.request.GET.get("type") provider_type = self.request.GET.get("type")
model = next(x for x in all_subclasses(Inlet) if x.__name__ == inlet_type) model = next(x for x in all_subclasses(Provider) if x.__name__ == provider_type)
if not model: if not model:
raise Http404 raise Http404
return path_to_class(model.form) return path_to_class(model.form)
class InletUpdateView( class ProviderUpdateView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
): ):
"""Update inlet""" """Update provider"""
model = Inlet model = Provider
permission_required = "passbook_core.change_inlet" permission_required = "passbook_core.change_provider"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("passbook_admin:inlets") success_url = reverse_lazy("passbook_admin:providers")
success_message = _("Successfully updated Inlet") success_message = _("Successfully updated Provider")
def get_form_class(self): def get_form_class(self):
form_class_path = self.get_object().form form_class_path = self.get_object().form
@ -77,25 +77,29 @@ class InletUpdateView(
def get_object(self, queryset=None): def get_object(self, queryset=None):
return ( return (
Inlet.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first() Provider.objects.filter(pk=self.kwargs.get("pk"))
.select_subclasses()
.first()
) )
class InletDeleteView( class ProviderDeleteView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
): ):
"""Delete inlet""" """Delete provider"""
model = Inlet model = Provider
permission_required = "passbook_core.delete_inlet" permission_required = "passbook_core.delete_provider"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("passbook_admin:inlets") success_url = reverse_lazy("passbook_admin:providers")
success_message = _("Successfully deleted Inlet") success_message = _("Successfully deleted Provider")
def get_object(self, queryset=None): def get_object(self, queryset=None):
return ( return (
Inlet.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first() Provider.objects.filter(pk=self.kwargs.get("pk"))
.select_subclasses()
.first()
) )
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):

View File

@ -1,4 +1,4 @@
"""passbook Outlet administration""" """passbook Source administration"""
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import ( from django.contrib.auth.mixins import (
@ -11,23 +11,23 @@ from django.utils.translation import ugettext as _
from django.views.generic import DeleteView, ListView, UpdateView from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.core.models import Outlet from passbook.core.models import Source
from passbook.lib.utils.reflection import all_subclasses, path_to_class from passbook.lib.utils.reflection import all_subclasses, path_to_class
from passbook.lib.views import CreateAssignPermView from passbook.lib.views import CreateAssignPermView
class OutletListView(LoginRequiredMixin, PermissionListMixin, ListView): class SourceListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all outlets""" """Show list of all sources"""
model = Outlet model = Source
permission_required = "passbook_core.add_outlet" permission_required = "passbook_core.view_source"
template_name = "administration/outlet/list.html" ordering = "name"
paginate_by = 10 paginate_by = 40
ordering = "id" template_name = "administration/source/list.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs["types"] = { kwargs["types"] = {
x.__name__: x._meta.verbose_name for x in all_subclasses(Outlet) x.__name__: x._meta.verbose_name for x in all_subclasses(Source)
} }
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
@ -35,40 +35,40 @@ class OutletListView(LoginRequiredMixin, PermissionListMixin, ListView):
return super().get_queryset().select_subclasses() return super().get_queryset().select_subclasses()
class OutletCreateView( class SourceCreateView(
SuccessMessageMixin, SuccessMessageMixin,
LoginRequiredMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, DjangoPermissionRequiredMixin,
CreateAssignPermView, CreateAssignPermView,
): ):
"""Create new Outlet""" """Create new Source"""
model = Outlet model = Source
permission_required = "passbook_core.add_outlet" permission_required = "passbook_core.add_source"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("passbook_admin:outlets") success_url = reverse_lazy("passbook_admin:sources")
success_message = _("Successfully created Outlet") success_message = _("Successfully created Source")
def get_form_class(self): def get_form_class(self):
outlet_type = self.request.GET.get("type") source_type = self.request.GET.get("type")
model = next(x for x in all_subclasses(Outlet) if x.__name__ == outlet_type) model = next(x for x in all_subclasses(Source) if x.__name__ == source_type)
if not model: if not model:
raise Http404 raise Http404
return path_to_class(model.form) return path_to_class(model.form)
class OutletUpdateView( class SourceUpdateView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
): ):
"""Update outlet""" """Update source"""
model = Outlet model = Source
permission_required = "passbook_core.change_outlet" permission_required = "passbook_core.change_source"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("passbook_admin:outlets") success_url = reverse_lazy("passbook_admin:sources")
success_message = _("Successfully updated Outlet") success_message = _("Successfully updated Source")
def get_form_class(self): def get_form_class(self):
form_class_path = self.get_object().form form_class_path = self.get_object().form
@ -77,25 +77,25 @@ class OutletUpdateView(
def get_object(self, queryset=None): def get_object(self, queryset=None):
return ( return (
Outlet.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first() Source.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
) )
class OutletDeleteView( class SourceDeleteView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
): ):
"""Delete outlet""" """Delete source"""
model = Outlet model = Source
permission_required = "passbook_core.delete_outlet" permission_required = "passbook_core.delete_source"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("passbook_admin:outlets") success_url = reverse_lazy("passbook_admin:sources")
success_message = _("Successfully deleted Outlet") success_message = _("Successfully deleted Source")
def get_object(self, queryset=None): def get_object(self, queryset=None):
return ( return (
Outlet.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first() Source.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
) )
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):

View File

@ -16,7 +16,7 @@ from guardian.mixins import (
) )
from passbook.admin.forms.users import UserForm from passbook.admin.forms.users import UserForm
from passbook.core.models import Token, User from passbook.core.models import Nonce, User
from passbook.lib.views import CreateAssignPermView from passbook.lib.views import CreateAssignPermView
@ -92,12 +92,12 @@ class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
permission_required = "passbook_core.reset_user_password" permission_required = "passbook_core.reset_user_password"
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Create token for user and return link""" """Create nonce for user and return link"""
super().get(request, *args, **kwargs) super().get(request, *args, **kwargs)
# TODO: create plan for user, get token # TODO: create plan for user, get token
token = Token.objects.create(user=self.object) nonce = Nonce.objects.create(user=self.object)
link = request.build_absolute_uri( link = request.build_absolute_uri(
reverse("passbook_flows:default-recovery", kwargs={"token": token.uuid}) reverse("passbook_flows:default-recovery", kwargs={"nonce": nonce.uuid})
) )
messages.success( messages.success(
request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link}) request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link})

View File

@ -1,8 +1,8 @@
"""permission classes for django restframework""" """permission classes for django restframework"""
from rest_framework.permissions import BasePermission, DjangoObjectPermissions from rest_framework.permissions import BasePermission, DjangoObjectPermissions
from passbook.core.models import PolicyModel
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
from passbook.policies.models import PolicyBindingModel
class CustomObjectPermissions(DjangoObjectPermissions): class CustomObjectPermissions(DjangoObjectPermissions):
@ -24,7 +24,8 @@ class PolicyPermissions(BasePermission):
policy_engine: PolicyEngine policy_engine: PolicyEngine
def has_object_permission(self, request, view, obj: PolicyBindingModel) -> bool: def has_object_permission(self, request, view, obj: PolicyModel) -> bool:
self.policy_engine = PolicyEngine(obj.policies.all(), request.user, request) # if not obj.po
self.policy_engine = PolicyEngine(obj.policies, request.user, request)
self.policy_engine.request.obj = obj self.policy_engine.request.obj = obj
return self.policy_engine.build().passing return self.policy_engine.build().passing

View File

@ -9,18 +9,12 @@ from structlog import get_logger
from passbook.api.permissions import CustomObjectPermissions from passbook.api.permissions import CustomObjectPermissions
from passbook.audit.api import EventViewSet from passbook.audit.api import EventViewSet
from passbook.channels.in_ldap.api import LDAPInletViewSet, LDAPPropertyMappingViewSet
from passbook.channels.in_oauth.api import OAuthInletViewSet
from passbook.channels.out_app_gw.api import ApplicationGatewayOutletViewSet
from passbook.channels.out_oauth.api import OAuth2OutletViewSet
from passbook.channels.out_oidc.api import OpenIDOutletViewSet
from passbook.channels.out_saml.api import SAMLOutletViewSet, SAMLPropertyMappingViewSet
from passbook.core.api.applications import ApplicationViewSet from passbook.core.api.applications import ApplicationViewSet
from passbook.core.api.groups import GroupViewSet from passbook.core.api.groups import GroupViewSet
from passbook.core.api.inlets import InletViewSet
from passbook.core.api.outlets import OutletViewSet
from passbook.core.api.policies import PolicyViewSet from passbook.core.api.policies import PolicyViewSet
from passbook.core.api.propertymappings import PropertyMappingViewSet from passbook.core.api.propertymappings import PropertyMappingViewSet
from passbook.core.api.providers import ProviderViewSet
from passbook.core.api.sources import SourceViewSet
from passbook.core.api.users import UserViewSet from passbook.core.api.users import UserViewSet
from passbook.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet from passbook.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet
from passbook.lib.utils.reflection import get_apps from passbook.lib.utils.reflection import get_apps
@ -30,6 +24,12 @@ from passbook.policies.expression.api import ExpressionPolicyViewSet
from passbook.policies.hibp.api import HaveIBeenPwendPolicyViewSet from passbook.policies.hibp.api import HaveIBeenPwendPolicyViewSet
from passbook.policies.password.api import PasswordPolicyViewSet from passbook.policies.password.api import PasswordPolicyViewSet
from passbook.policies.reputation.api import ReputationPolicyViewSet from passbook.policies.reputation.api import ReputationPolicyViewSet
from passbook.providers.app_gw.api import ApplicationGatewayProviderViewSet
from passbook.providers.oauth.api import OAuth2ProviderViewSet
from passbook.providers.oidc.api import OpenIDProviderViewSet
from passbook.providers.saml.api import SAMLPropertyMappingViewSet, SAMLProviderViewSet
from passbook.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet
from passbook.sources.oauth.api import OAuthSourceViewSet
from passbook.stages.captcha.api import CaptchaStageViewSet from passbook.stages.captcha.api import CaptchaStageViewSet
from passbook.stages.email.api import EmailStageViewSet from passbook.stages.email.api import EmailStageViewSet
from passbook.stages.identification.api import IdentificationStageViewSet from passbook.stages.identification.api import IdentificationStageViewSet
@ -57,15 +57,9 @@ router.register("core/users", UserViewSet)
router.register("audit/events", EventViewSet) router.register("audit/events", EventViewSet)
router.register("inlets/all", InletViewSet) router.register("sources/all", SourceViewSet)
router.register("inlets/ldap", LDAPInletViewSet) router.register("sources/ldap", LDAPSourceViewSet)
router.register("inlets/oauth", OAuthInletViewSet) router.register("sources/oauth", OAuthSourceViewSet)
router.register("outlets/all", OutletViewSet)
router.register("outlets/applicationgateway", ApplicationGatewayOutletViewSet)
router.register("outlets/oauth", OAuth2OutletViewSet)
router.register("outlets/openid", OpenIDOutletViewSet)
router.register("outlets/saml", SAMLOutletViewSet)
router.register("policies/all", PolicyViewSet) router.register("policies/all", PolicyViewSet)
router.register("policies/bindings", PolicyBindingViewSet) router.register("policies/bindings", PolicyBindingViewSet)
@ -75,6 +69,12 @@ router.register("policies/password", PasswordPolicyViewSet)
router.register("policies/passwordexpiry", PasswordExpiryPolicyViewSet) router.register("policies/passwordexpiry", PasswordExpiryPolicyViewSet)
router.register("policies/reputation", ReputationPolicyViewSet) router.register("policies/reputation", ReputationPolicyViewSet)
router.register("providers/all", ProviderViewSet)
router.register("providers/applicationgateway", ApplicationGatewayProviderViewSet)
router.register("providers/oauth", OAuth2ProviderViewSet)
router.register("providers/openid", OpenIDProviderViewSet)
router.register("providers/saml", SAMLProviderViewSet)
router.register("propertymappings/all", PropertyMappingViewSet) router.register("propertymappings/all", PropertyMappingViewSet)
router.register("propertymappings/ldap", LDAPPropertyMappingViewSet) router.register("propertymappings/ldap", LDAPPropertyMappingViewSet)
router.register("propertymappings/saml", SAMLPropertyMappingViewSet) router.register("propertymappings/saml", SAMLPropertyMappingViewSet)

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:58 # Generated by Django 2.2.6 on 2019-10-07 14:07
import uuid import uuid
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name="Event", name="AuditEntry",
fields=[ fields=[
( (
"uuid", "uuid",
@ -33,16 +33,15 @@ class Migration(migrations.Migration):
"action", "action",
models.TextField( models.TextField(
choices=[ choices=[
("LOGIN", "login"), ("login", "login"),
("LOGIN_FAILED", "login_failed"), ("login_failed", "login_failed"),
("LOGOUT", "logout"), ("logout", "logout"),
("AUTHORIZE_APPLICATION", "authorize_application"), ("authorize_application", "authorize_application"),
("SUSPICIOUS_REQUEST", "suspicious_request"), ("suspicious_request", "suspicious_request"),
("SIGN_UP", "sign_up"), ("sign_up", "sign_up"),
("PASSWORD_RESET", "password_reset"), ("password_reset", "password_reset"),
("INVITE_CREATED", "invitation_created"), ("invitation_created", "invitation_created"),
("INVITE_USED", "invitation_used"), ("invitation_used", "invitation_used"),
("CUSTOM", "custom"),
] ]
), ),
), ),
@ -54,7 +53,7 @@ class Migration(migrations.Migration):
blank=True, default=dict blank=True, default=dict
), ),
), ),
("client_ip", models.GenericIPAddressField(null=True)), ("request_ip", models.GenericIPAddressField()),
("created", models.DateTimeField(auto_now_add=True)), ("created", models.DateTimeField(auto_now_add=True)),
( (
"user", "user",
@ -66,8 +65,8 @@ class Migration(migrations.Migration):
), ),
], ],
options={ options={
"verbose_name": "Audit Event", "verbose_name": "Audit Entry",
"verbose_name_plural": "Audit Events", "verbose_name_plural": "Audit Entries",
}, },
), ),
] ]

View File

@ -0,0 +1,16 @@
# Generated by Django 2.2.6 on 2019-10-28 08:29
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("passbook_audit", "0001_initial"),
]
operations = [
migrations.RenameModel(old_name="AuditEntry", new_name="Event",),
]

View File

@ -0,0 +1,40 @@
# Generated by Django 2.2.8 on 2019-12-05 14:07
from django.db import migrations, models
import passbook.audit.models
class Migration(migrations.Migration):
dependencies = [
("passbook_audit", "0002_auto_20191028_0829"),
]
operations = [
migrations.AlterModelOptions(
name="event",
options={
"verbose_name": "Audit Event",
"verbose_name_plural": "Audit Events",
},
),
migrations.AlterField(
model_name="event",
name="action",
field=models.TextField(
choices=[
("LOGIN", "login"),
("LOGIN_FAILED", "login_failed"),
("LOGOUT", "logout"),
("AUTHORIZE_APPLICATION", "authorize_application"),
("SUSPICIOUS_REQUEST", "suspicious_request"),
("SIGN_UP", "sign_up"),
("PASSWORD_RESET", "password_reset"),
("INVITE_CREATED", "invitation_created"),
("INVITE_USED", "invitation_used"),
("CUSTOM", "custom"),
]
),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.8 on 2019-12-05 15:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_audit", "0003_auto_20191205_1407"),
]
operations = [
migrations.RemoveField(model_name="event", name="request_ip",),
migrations.AddField(
model_name="event",
name="client_ip",
field=models.GenericIPAddressField(null=True),
),
]

View File

@ -5,7 +5,7 @@ from django.test import TestCase
from guardian.shortcuts import get_anonymous_user from guardian.shortcuts import get_anonymous_user
from passbook.audit.models import Event, EventAction from passbook.audit.models import Event, EventAction
from passbook.policies.models import Policy from passbook.core.models import Policy
class TestAuditEvent(TestCase): class TestAuditEvent(TestCase):

View File

@ -1,11 +0,0 @@
"""Passbook ldap app config"""
from django.apps import AppConfig
class PassbookInletLDAPConfig(AppConfig):
"""Passbook ldap app config"""
name = "passbook.channels.in_ldap"
label = "passbook_channels_in_ldap"
verbose_name = "passbook Inlets.LDAP"

View File

@ -1,33 +0,0 @@
"""LDAP Sync tasks"""
from passbook.channels.in_ldap.connector import Connector
from passbook.channels.in_ldap.models import LDAPInlet
from passbook.root.celery import CELERY_APP
@CELERY_APP.task()
def sync_groups(inlet_pk: int):
"""Sync LDAP Groups on background worker"""
inlet = LDAPInlet.objects.get(pk=inlet_pk)
connector = Connector(inlet)
connector.bind()
connector.sync_groups()
@CELERY_APP.task()
def sync_users(inlet_pk: int):
"""Sync LDAP Users on background worker"""
inlet = LDAPInlet.objects.get(pk=inlet_pk)
connector = Connector(inlet)
connector.bind()
connector.sync_users()
@CELERY_APP.task()
def sync():
"""Sync all inlets"""
for inlet in LDAPInlet.objects.filter(enabled=True):
connector = Connector(inlet)
connector.bind()
connector.sync_users()
connector.sync_groups()
connector.sync_membership()

View File

@ -1,29 +0,0 @@
"""OAuth Inlet Serializer"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.admin.forms.inlet import INLET_SERIALIZER_FIELDS
from passbook.channels.in_oauth.models import OAuthInlet
class OAuthInletSerializer(ModelSerializer):
"""OAuth Inlet Serializer"""
class Meta:
model = OAuthInlet
fields = INLET_SERIALIZER_FIELDS + [
"inlet_type",
"request_token_url",
"authorization_url",
"access_token_url",
"profile_url",
"consumer_key",
"consumer_secret",
]
class OAuthInletViewSet(ModelViewSet):
"""Inlet Viewset"""
queryset = OAuthInlet.objects.all()
serializer_class = OAuthInletSerializer

View File

@ -1,24 +0,0 @@
"""passbook oauth_client Authorization backend"""
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from passbook.channels.in_oauth.models import OAuthInlet, UserOAuthInletConnection
class AuthorizedServiceBackend(ModelBackend):
"Authentication backend for users registered with remote OAuth provider."
def authenticate(self, request, inlet=None, identifier=None):
"Fetch user for a given inlet by id."
inlet_q = Q(inlet__name=inlet)
if isinstance(inlet, OAuthInlet):
inlet_q = Q(inlet=inlet)
try:
access = UserOAuthInletConnection.objects.filter(
inlet_q, identifier=identifier
).select_related("user")[0]
except IndexError:
return None
else:
return access.user

View File

@ -1,81 +0,0 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("passbook_core", "__first__"),
]
operations = [
migrations.CreateModel(
name="OAuthInlet",
fields=[
(
"inlet_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_core.Inlet",
),
),
("inlet_type", models.CharField(max_length=255)),
(
"request_token_url",
models.CharField(
blank=True, max_length=255, verbose_name="Request Token URL"
),
),
(
"authorization_url",
models.CharField(max_length=255, verbose_name="Authorization URL"),
),
(
"access_token_url",
models.CharField(max_length=255, verbose_name="Access Token URL"),
),
(
"profile_url",
models.CharField(max_length=255, verbose_name="Profile URL"),
),
("consumer_key", models.TextField()),
("consumer_secret", models.TextField()),
],
options={
"verbose_name": "Generic OAuth Inlet",
"verbose_name_plural": "Generic OAuth Inlets",
},
bases=("passbook_core.inlet",),
),
migrations.CreateModel(
name="UserOAuthInletConnection",
fields=[
(
"userinletconnection_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_core.UserInletConnection",
),
),
("identifier", models.CharField(max_length=255)),
("access_token", models.TextField(blank=True, default=None, null=True)),
],
options={
"verbose_name": "User OAuth Inlet Connection",
"verbose_name_plural": "User OAuth Inlet Connections",
},
bases=("passbook_core.userinletconnection",),
),
]

View File

@ -1,159 +0,0 @@
"""OAuth Client models"""
from django.db import models
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
from passbook.channels.in_oauth.clients import get_client
from passbook.core.models import Inlet, UserInletConnection
from passbook.core.types import UILoginButton, UIUserSettings
class OAuthInlet(Inlet):
"""Configuration for OAuth inlet."""
inlet_type = models.CharField(max_length=255)
request_token_url = models.CharField(
blank=True, max_length=255, verbose_name=_("Request Token URL")
)
authorization_url = models.CharField(
max_length=255, verbose_name=_("Authorization URL")
)
access_token_url = models.CharField(
max_length=255, verbose_name=_("Access Token URL")
)
profile_url = models.CharField(max_length=255, verbose_name=_("Profile URL"))
consumer_key = models.TextField()
consumer_secret = models.TextField()
form = "passbook.channels.in_oauth.forms.OAuthInletForm"
@property
def ui_login_button(self) -> UILoginButton:
return UILoginButton(
url=reverse_lazy(
"passbook_channels_in_oauth:oauth-client-login",
kwargs={"inlet_slug": self.slug},
),
icon_path=f"passbook/inlets/{self.inlet_type}.svg",
name=self.name,
)
@property
def ui_additional_info(self) -> str:
url = reverse_lazy(
"passbook_channels_in_oauth:oauth-client-callback",
kwargs={"inlet_slug": self.slug},
)
return f"Callback URL: <pre>{url}</pre>"
@property
def ui_user_settings(self) -> UIUserSettings:
icon_type = self.inlet_type
if icon_type == "azure ad":
icon_type = "windows"
icon_class = f"fab fa-{icon_type}"
view_name = "passbook_channels_in_oauth:oauth-client-user"
return UIUserSettings(
name=self.name,
icon=icon_class,
view_name=reverse((view_name), kwargs={"inlet_slug": self.slug}),
)
class Meta:
verbose_name = _("Generic OAuth Inlet")
verbose_name_plural = _("Generic OAuth Inlets")
class GitHubOAuthInlet(OAuthInlet):
"""Abstract subclass of OAuthInlet to specify GitHub Form"""
form = "passbook.channels.in_oauth.forms.GitHubOAuthInletForm"
class Meta:
abstract = True
verbose_name = _("GitHub OAuth Inlet")
verbose_name_plural = _("GitHub OAuth Inlets")
class TwitterOAuthInlet(OAuthInlet):
"""Abstract subclass of OAuthInlet to specify Twitter Form"""
form = "passbook.channels.in_oauth.forms.TwitterOAuthInletForm"
class Meta:
abstract = True
verbose_name = _("Twitter OAuth Inlet")
verbose_name_plural = _("Twitter OAuth Inlets")
class FacebookOAuthInlet(OAuthInlet):
"""Abstract subclass of OAuthInlet to specify Facebook Form"""
form = "passbook.channels.in_oauth.forms.FacebookOAuthInletForm"
class Meta:
abstract = True
verbose_name = _("Facebook OAuth Inlet")
verbose_name_plural = _("Facebook OAuth Inlets")
class DiscordOAuthInlet(OAuthInlet):
"""Abstract subclass of OAuthInlet to specify Discord Form"""
form = "passbook.channels.in_oauth.forms.DiscordOAuthInletForm"
class Meta:
abstract = True
verbose_name = _("Discord OAuth Inlet")
verbose_name_plural = _("Discord OAuth Inlets")
class GoogleOAuthInlet(OAuthInlet):
"""Abstract subclass of OAuthInlet to specify Google Form"""
form = "passbook.channels.in_oauth.forms.GoogleOAuthInletForm"
class Meta:
abstract = True
verbose_name = _("Google OAuth Inlet")
verbose_name_plural = _("Google OAuth Inlets")
class AzureADOAuthInlet(OAuthInlet):
"""Abstract subclass of OAuthInlet to specify AzureAD Form"""
form = "passbook.channels.in_oauth.forms.AzureADOAuthInletForm"
class Meta:
abstract = True
verbose_name = _("Azure AD OAuth Inlet")
verbose_name_plural = _("Azure AD OAuth Inlets")
class UserOAuthInletConnection(UserInletConnection):
"""Authorized remote OAuth inlet."""
identifier = models.CharField(max_length=255)
access_token = models.TextField(blank=True, null=True, default=None)
def save(self, *args, **kwargs):
self.access_token = self.access_token or None
super().save(*args, **kwargs)
@property
def api_client(self):
"""Get API Client"""
return get_client(self.inlet, self.access_token or "")
class Meta:
verbose_name = _("User OAuth Inlet Connection")
verbose_name_plural = _("User OAuth Inlet Connections")

View File

@ -1,15 +0,0 @@
"""Oauth2 Client Settings"""
AUTHENTICATION_BACKENDS = [
"passbook.channels.in_oauth.backends.AuthorizedServiceBackend",
]
PASSBOOK_SOURCES_OAUTH_TYPES = [
"passbook.channels.in_oauth.types.discord",
"passbook.channels.in_oauth.types.facebook",
"passbook.channels.in_oauth.types.github",
"passbook.channels.in_oauth.types.google",
"passbook.channels.in_oauth.types.reddit",
"passbook.channels.in_oauth.types.twitter",
"passbook.channels.in_oauth.types.azure_ad",
]

View File

@ -1,28 +0,0 @@
"""SAMLInlet API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.channels.in_saml.models import SAMLInlet
class SAMLInletSerializer(ModelSerializer):
"""SAMLInlet Serializer"""
class Meta:
model = SAMLInlet
fields = [
"pk",
"issuer",
"idp_url",
"idp_logout_url",
"auto_logout",
"signing_kp",
]
class SAMLInletViewSet(ModelViewSet):
"""SAMLInlet Viewset"""
queryset = SAMLInlet.objects.all()
serializer_class = SAMLInletSerializer

View File

@ -1,12 +0,0 @@
"""Passbook SAML app config"""
from django.apps import AppConfig
class PassbookInletSAMLConfig(AppConfig):
"""passbook saml_idp app config"""
name = "passbook.channels.in_saml"
label = "passbook_channels_in_saml"
verbose_name = "passbook Inlets.SAML"
mountpoint = "source/saml/"

View File

@ -1,68 +0,0 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("passbook_crypto", "0001_initial"),
("passbook_core", "__first__"),
]
operations = [
migrations.CreateModel(
name="SAMLInlet",
fields=[
(
"inlet_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_core.Inlet",
),
),
(
"issuer",
models.TextField(
blank=True,
default=None,
help_text="Also known as Entity ID. Defaults the Metadata URL.",
verbose_name="Issuer",
),
),
("idp_url", models.URLField(verbose_name="IDP URL")),
(
"idp_logout_url",
models.URLField(
blank=True,
default=None,
null=True,
verbose_name="IDP Logout URL",
),
),
("auto_logout", models.BooleanField(default=False)),
(
"signing_kp",
models.ForeignKey(
default=None,
help_text="Certificate Key Pair of the IdP which Assertions are validated against.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="passbook_crypto.CertificateKeyPair",
),
),
],
options={
"verbose_name": "SAML Inlet",
"verbose_name_plural": "SAML Inlets",
},
bases=("passbook_core.inlet",),
),
]

View File

@ -1,20 +0,0 @@
"""saml sp helpers"""
from django.http import HttpRequest
from django.shortcuts import reverse
from passbook.channels.in_saml.models import SAMLInlet
def get_issuer(request: HttpRequest, inlet: SAMLInlet) -> str:
"""Get Inlet's Issuer, falling back to our Metadata URL if none is set"""
issuer = inlet.issuer
if issuer is None:
return build_full_url("metadata", request, inlet)
return issuer
def build_full_url(view: str, request: HttpRequest, inlet: SAMLInlet) -> str:
"""Build Full ACS URL to be used in IDP"""
return request.build_absolute_uri(
reverse(f"passbook_channels_in_saml:{view}", kwargs={"inlet_slug": inlet.slug})
)

View File

@ -1,29 +0,0 @@
"""OAuth2Outlet API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.channels.out_oauth.models import OAuth2Outlet
class OAuth2OutletSerializer(ModelSerializer):
"""OAuth2Outlet Serializer"""
class Meta:
model = OAuth2Outlet
fields = [
"pk",
"name",
"redirect_uris",
"client_type",
"authorization_grant_type",
"client_id",
"client_secret",
]
class OAuth2OutletViewSet(ModelViewSet):
"""OAuth2Outlet Viewset"""
queryset = OAuth2Outlet.objects.all()
serializer_class = OAuth2OutletSerializer

View File

@ -1,12 +0,0 @@
"""passbook auth oauth provider app config"""
from django.apps import AppConfig
class PassbookOutletOAuthConfig(AppConfig):
"""passbook auth oauth provider app config"""
name = "passbook.channels.out_oauth"
label = "passbook_channels_out_oauth"
verbose_name = "passbook Outlets.OAuth"
mountpoint = ""

View File

@ -1,9 +0,0 @@
"""passbook OIDC Provider"""
INSTALLED_APPS = [
"oidc_provider",
]
OIDC_AFTER_USERLOGIN_HOOK = "passbook.channels.out_oidc.auth.check_permissions"
OIDC_IDTOKEN_INCLUDE_CLAIMS = True
OIDC_USERINFO = "passbook.channels.out_oidc.claims.userinfo"

View File

@ -1,140 +0,0 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59
import django.db.models.deletion
from django.db import migrations, models
import passbook.channels.out_saml.utils.time
class Migration(migrations.Migration):
initial = True
dependencies = [
("passbook_core", "__first__"),
("passbook_crypto", "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(verbose_name="SAML Name")),
(
"friendly_name",
models.TextField(blank=True, default=None, null=True),
),
],
options={
"verbose_name": "SAML Property Mapping",
"verbose_name_plural": "SAML Property Mappings",
},
bases=("passbook_core.propertymapping",),
),
migrations.CreateModel(
name="SAMLOutlet",
fields=[
(
"outlet_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_core.Outlet",
),
),
("name", models.TextField()),
("processor_path", models.CharField(choices=[], max_length=255)),
("acs_url", models.URLField(verbose_name="ACS URL")),
("audience", models.TextField(default="")),
("issuer", models.TextField(help_text="Also known as EntityID")),
(
"assertion_valid_not_before",
models.TextField(
default="minutes=-5",
help_text="Assertion valid not before current time + this value (Format: hours=-1;minutes=-2;seconds=-3).",
validators=[
passbook.channels.out_saml.utils.time.timedelta_string_validator
],
),
),
(
"assertion_valid_not_on_or_after",
models.TextField(
default="minutes=5",
help_text="Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).",
validators=[
passbook.channels.out_saml.utils.time.timedelta_string_validator
],
),
),
(
"session_valid_not_on_or_after",
models.TextField(
default="minutes=86400",
help_text="Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).",
validators=[
passbook.channels.out_saml.utils.time.timedelta_string_validator
],
),
),
(
"digest_algorithm",
models.CharField(
choices=[("sha1", "SHA1"), ("sha256", "SHA256")],
default="sha256",
max_length=50,
),
),
(
"signature_algorithm",
models.CharField(
choices=[
("rsa-sha1", "RSA-SHA1"),
("rsa-sha256", "RSA-SHA256"),
("ecdsa-sha256", "ECDSA-SHA256"),
("dsa-sha1", "DSA-SHA1"),
],
default="rsa-sha256",
max_length=50,
),
),
(
"require_signing",
models.BooleanField(
default=False,
help_text="Require Requests to be signed by an X509 Certificate. Must match the Certificate selected in `Singing Keypair`.",
),
),
(
"signing_kp",
models.ForeignKey(
default=None,
help_text="Singing is enabled upon selection of a Key Pair.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="passbook_crypto.CertificateKeyPair",
verbose_name="Signing Keypair",
),
),
],
options={
"verbose_name": "SAML Outlet",
"verbose_name_plural": "SAML Outlets",
},
bases=("passbook_core.outlet",),
),
]

View File

@ -1,6 +0,0 @@
"""saml provider settings"""
PASSBOOK_PROVIDERS_SAML_PROCESSORS = [
"passbook.channels.out_saml.processors.generic",
"passbook.channels.out_saml.processors.salesforce",
]

View File

@ -1,11 +0,0 @@
"""passbook saml provider app config"""
from django.apps import AppConfig
class PassbookOutletSAMLv2Config(AppConfig):
"""passbook samlv2 provider app config"""
name = "passbook.channels.out_samlv2"
label = "passbook_channels_out_samlv2"
verbose_name = "passbook Outlets.SAMLv2"
mountpoint = "application/samlv2/"

View File

@ -16,7 +16,7 @@ class ApplicationSerializer(ModelSerializer):
"name", "name",
"slug", "slug",
"skip_authorization", "skip_authorization",
"outlet", "provider",
"meta_launch_url", "meta_launch_url",
"meta_icon_url", "meta_icon_url",
"meta_description", "meta_description",

View File

@ -1,31 +0,0 @@
"""Inlet API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.admin.forms.inlet import INLET_SERIALIZER_FIELDS
from passbook.core.models import Inlet
class InletSerializer(ModelSerializer):
"""Inlet Serializer"""
__type__ = SerializerMethodField(method_name="get_type")
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace("inlet", "")
class Meta:
model = Inlet
fields = INLET_SERIALIZER_FIELDS + ["__type__"]
class InletViewSet(ReadOnlyModelViewSet):
"""Inlet Viewset"""
queryset = Inlet.objects.all()
serializer_class = InletSerializer
def get_queryset(self):
return Inlet.objects.select_subclasses()

View File

@ -1,30 +0,0 @@
"""Outlet API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.core.models import Outlet
class OutletSerializer(ModelSerializer):
"""Outlet Serializer"""
__type__ = SerializerMethodField(method_name="get_type")
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace("outlet", "")
class Meta:
model = Outlet
fields = ["pk", "property_mappings", "__type__"]
class OutletViewSet(ReadOnlyModelViewSet):
"""Outlet Viewset"""
queryset = Outlet.objects.all()
serializer_class = OutletSerializer
def get_queryset(self):
return Outlet.objects.select_subclasses()

View File

@ -2,8 +2,8 @@
from rest_framework.serializers import ModelSerializer, SerializerMethodField from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.core.models import Policy
from passbook.policies.forms import GENERAL_FIELDS from passbook.policies.forms import GENERAL_FIELDS
from passbook.policies.models import Policy
class PolicySerializer(ModelSerializer): class PolicySerializer(ModelSerializer):

View File

@ -0,0 +1,30 @@
"""Provider API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.core.models import Provider
class ProviderSerializer(ModelSerializer):
"""Provider Serializer"""
__type__ = SerializerMethodField(method_name="get_type")
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace("provider", "")
class Meta:
model = Provider
fields = ["pk", "property_mappings", "__type__"]
class ProviderViewSet(ReadOnlyModelViewSet):
"""Provider Viewset"""
queryset = Provider.objects.all()
serializer_class = ProviderSerializer
def get_queryset(self):
return Provider.objects.select_subclasses()

View File

@ -0,0 +1,31 @@
"""Source API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.admin.forms.source import SOURCE_SERIALIZER_FIELDS
from passbook.core.models import Source
class SourceSerializer(ModelSerializer):
"""Source Serializer"""
__type__ = SerializerMethodField(method_name="get_type")
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace("source", "")
class Meta:
model = Source
fields = SOURCE_SERIALIZER_FIELDS + ["__type__"]
class SourceViewSet(ReadOnlyModelViewSet):
"""Source Viewset"""
queryset = Source.objects.all()
serializer_class = SourceSerializer
def get_queryset(self):
return Source.objects.select_subclasses()

View File

@ -3,14 +3,14 @@ from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from passbook.core.models import Application, Outlet from passbook.core.models import Application, Provider
class ApplicationForm(forms.ModelForm): class ApplicationForm(forms.ModelForm):
"""Application Form""" """Application Form"""
outlet = forms.ModelChoiceField( provider = forms.ModelChoiceField(
queryset=Outlet.objects.all().order_by("pk").select_subclasses(), queryset=Provider.objects.all().order_by("pk").select_subclasses(),
required=False, required=False,
) )
@ -21,7 +21,7 @@ class ApplicationForm(forms.ModelForm):
"name", "name",
"slug", "slug",
"skip_authorization", "skip_authorization",
"outlet", "provider",
"meta_launch_url", "meta_launch_url",
"meta_icon_url", "meta_icon_url",
"meta_description", "meta_description",

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59 # Generated by Django 2.2.6 on 2019-10-07 14:06
import uuid import uuid
@ -7,7 +7,6 @@ import django.contrib.auth.validators
import django.contrib.postgres.fields.jsonb import django.contrib.postgres.fields.jsonb
import django.db.models.deletion import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
import guardian.mixins
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@ -20,7 +19,6 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
("auth", "0011_update_proxy_permissions"), ("auth", "0011_update_proxy_permissions"),
("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [
@ -107,41 +105,63 @@ class Migration(migrations.Migration):
), ),
), ),
("uuid", models.UUIDField(default=uuid.uuid4, editable=False)), ("uuid", models.UUIDField(default=uuid.uuid4, editable=False)),
("name", models.TextField(help_text="User's display name.")), ("name", models.TextField()),
("password_change_date", models.DateTimeField(auto_now_add=True)), ("password_change_date", models.DateTimeField(auto_now_add=True)),
(
"attributes",
django.contrib.postgres.fields.jsonb.JSONField(
blank=True, default=dict
),
),
], ],
options={"permissions": (("reset_user_password", "Reset Password"),),}, options={
bases=(guardian.mixins.GuardianUserMixin, models.Model), "verbose_name": "user",
"verbose_name_plural": "users",
"abstract": False,
},
managers=[("objects", django.contrib.auth.models.UserManager()),], managers=[("objects", django.contrib.auth.models.UserManager()),],
), ),
migrations.CreateModel( migrations.CreateModel(
name="Inlet", name="Policy",
fields=[ fields=[
("created", models.DateTimeField(auto_now_add=True)),
("last_updated", models.DateTimeField(auto_now=True)),
( (
"policybindingmodel_ptr", "uuid",
models.OneToOneField( models.UUIDField(
auto_created=True, default=uuid.uuid4,
on_delete=django.db.models.deletion.CASCADE, editable=False,
parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_policies.PolicyBindingModel",
), ),
), ),
("name", models.TextField(help_text="Inlet's display Name.")), ("name", models.TextField(blank=True, null=True)),
( (
"slug", "action",
models.SlugField(help_text="Internal source name, used in URLs."), models.CharField(
choices=[("allow", "allow"), ("deny", "deny")], max_length=20
), ),
("enabled", models.BooleanField(default=True)), ),
("negate", models.BooleanField(default=False)),
("order", models.IntegerField(default=0)),
("timeout", models.IntegerField(default=30)),
], ],
bases=("passbook_policies.policybindingmodel",), options={"abstract": False,},
),
migrations.CreateModel(
name="PolicyModel",
fields=[
("created", models.DateTimeField(auto_now_add=True)),
("last_updated", models.DateTimeField(auto_now=True)),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"policies",
models.ManyToManyField(blank=True, to="passbook_core.Policy"),
),
],
options={"abstract": False,},
), ),
migrations.CreateModel( migrations.CreateModel(
name="PropertyMapping", name="PropertyMapping",
@ -156,7 +176,6 @@ class Migration(migrations.Migration):
), ),
), ),
("name", models.TextField()), ("name", models.TextField()),
("expression", models.TextField()),
], ],
options={ options={
"verbose_name": "Property Mapping", "verbose_name": "Property Mapping",
@ -164,38 +183,74 @@ class Migration(migrations.Migration):
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name="UserInletConnection", name="DebugPolicy",
fields=[ fields=[
( (
"id", "policy_ptr",
models.AutoField( models.OneToOneField(
auto_created=True, auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
verbose_name="ID", to="passbook_core.Policy",
),
),
("created", models.DateTimeField(auto_now_add=True)),
("last_updated", models.DateTimeField(auto_now=True)),
(
"inlet",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="passbook_core.Inlet",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
), ),
), ),
("result", models.BooleanField(default=False)),
("wait_min", models.IntegerField(default=5)),
("wait_max", models.IntegerField(default=30)),
], ],
options={"unique_together": {("user", "inlet")},}, options={
"verbose_name": "Debug Policy",
"verbose_name_plural": "Debug Policies",
},
bases=("passbook_core.policy",),
), ),
migrations.CreateModel( migrations.CreateModel(
name="Outlet", 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=[ fields=[
( (
"id", "id",
@ -215,7 +270,7 @@ class Migration(migrations.Migration):
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name="Token", name="Nonce",
fields=[ fields=[
( (
"uuid", "uuid",
@ -229,11 +284,10 @@ class Migration(migrations.Migration):
( (
"expires", "expires",
models.DateTimeField( models.DateTimeField(
default=passbook.core.models.default_token_duration default=passbook.core.models.default_nonce_duration
), ),
), ),
("expiring", models.BooleanField(default=True)), ("expiring", models.BooleanField(default=True)),
("description", models.TextField(blank=True, default="")),
( (
"user", "user",
models.ForeignKey( models.ForeignKey(
@ -242,15 +296,37 @@ class Migration(migrations.Migration):
), ),
), ),
], ],
options={"verbose_name": "Token", "verbose_name_plural": "Tokens",}, options={"verbose_name": "Nonce", "verbose_name_plural": "Nonces",},
), ),
migrations.AddField( migrations.CreateModel(
model_name="inlet", name="Invitation",
name="property_mappings", fields=[
field=models.ManyToManyField( (
blank=True, default=None, to="passbook_core.PropertyMapping" "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( migrations.CreateModel(
name="Group", name="Group",
fields=[ fields=[
@ -265,7 +341,7 @@ class Migration(migrations.Migration):
), ),
("name", models.CharField(max_length=80, verbose_name="name")), ("name", models.CharField(max_length=80, verbose_name="name")),
( (
"attributes", "tags",
django.contrib.postgres.fields.jsonb.JSONField( django.contrib.postgres.fields.jsonb.JSONField(
blank=True, default=dict blank=True, default=dict
), ),
@ -283,57 +359,11 @@ class Migration(migrations.Migration):
], ],
options={"unique_together": {("name", "parent")},}, options={"unique_together": {("name", "parent")},},
), ),
migrations.CreateModel(
name="Application",
fields=[
(
"policybindingmodel_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_policies.PolicyBindingModel",
),
),
("name", models.TextField(help_text="Application's display Name.")),
(
"slug",
models.SlugField(
help_text="Internal application name, used in URLs."
),
),
("skip_authorization", models.BooleanField(default=False)),
("meta_launch_url", models.URLField(blank=True, default="")),
("meta_icon_url", models.TextField(blank=True, default="")),
("meta_description", models.TextField(blank=True, default="")),
("meta_publisher", models.TextField(blank=True, default="")),
(
"outlet",
models.OneToOneField(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
to="passbook_core.Outlet",
),
),
],
bases=("passbook_policies.policybindingmodel",),
),
migrations.AddField( migrations.AddField(
model_name="user", model_name="user",
name="groups", name="groups",
field=models.ManyToManyField(to="passbook_core.Group"), field=models.ManyToManyField(to="passbook_core.Group"),
), ),
migrations.AddField(
model_name="user",
name="inlets",
field=models.ManyToManyField(
through="passbook_core.UserInletConnection", to="passbook_core.Inlet"
),
),
migrations.AddField( migrations.AddField(
model_name="user", model_name="user",
name="user_permissions", name="user_permissions",
@ -347,33 +377,74 @@ class Migration(migrations.Migration):
), ),
), ),
migrations.CreateModel( migrations.CreateModel(
name="PropertyMappingBinding", name="UserSourceConnection",
fields=[ fields=[
( (
"uuid", "id",
models.UUIDField( models.AutoField(
default=uuid.uuid4, auto_created=True,
editable=False,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
verbose_name="ID",
), ),
), ),
("order", models.IntegerField(default=0)), ("created", models.DateTimeField(auto_now_add=True)),
("last_updated", models.DateTimeField(auto_now=True)),
( (
"outlet", "user",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
to="passbook_core.Outlet", to=settings.AUTH_USER_MODEL,
), ),
), ),
( (
"property_mapping", "source",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
to="passbook_core.PropertyMapping", to="passbook_core.Source",
), ),
), ),
], ],
options={"unique_together": {("property_mapping", "outlet", "order")},}, options={"unique_together": {("user", "source")},},
),
migrations.CreateModel(
name="Application",
fields=[
(
"policymodel_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_core.PolicyModel",
),
),
("name", models.TextField()),
("slug", models.SlugField()),
("launch_url", models.URLField(blank=True, null=True)),
("icon_url", models.TextField(blank=True, null=True)),
("skip_authorization", models.BooleanField(default=False)),
(
"provider",
models.OneToOneField(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
to="passbook_core.Provider",
),
),
],
options={"abstract": False,},
bases=("passbook_core.policymodel",),
),
migrations.AddField(
model_name="user",
name="sources",
field=models.ManyToManyField(
through="passbook_core.UserSourceConnection", to="passbook_core.Source"
),
), ),
] ]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.6 on 2019-10-10 10:58
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0001_initial"),
]
operations = [
migrations.AlterModelOptions(
name="user",
options={"permissions": (("reset_user_password", "Reset Password"),)},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.6 on 2019-10-10 11:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="nonce",
name="description",
field=models.TextField(blank=True, default=""),
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 2.2.6 on 2019-10-11 09:14
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0002_nonce_description"),
]
operations = [
migrations.RenameField(
model_name="group", old_name="tags", new_name="attributes",
),
migrations.AddField(
model_name="source",
name="property_mappings",
field=models.ManyToManyField(
blank=True, default=None, to="passbook_core.PropertyMapping"
),
),
migrations.AddField(
model_name="user",
name="attributes",
field=django.contrib.postgres.fields.jsonb.JSONField(
blank=True, default=dict
),
),
]

View File

@ -0,0 +1,13 @@
# Generated by Django 2.2.6 on 2019-10-10 15:41
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0002_auto_20191010_1058"),
("passbook_core", "0002_nonce_description"),
]
operations = []

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.6 on 2019-10-14 11:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0003_auto_20191011_0914"),
]
operations = [
migrations.RemoveField(model_name="policy", name="action",),
]

View File

@ -0,0 +1,13 @@
# Generated by Django 2.2.6 on 2019-10-25 20:22
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0004_remove_policy_action"),
("passbook_core", "0003_merge_20191010_1541"),
]
operations = []

View File

@ -0,0 +1,19 @@
# Generated by Django 3.0.3 on 2020-02-17 16:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0005_merge_20191025_2022"),
]
operations = [
migrations.AddField(
model_name="propertymapping",
name="template",
field=models.TextField(default=""),
preserve_default=False,
),
]

View File

@ -0,0 +1,16 @@
# Generated by Django 3.0.3 on 2020-02-17 19:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0006_propertymapping_template"),
]
operations = [
migrations.RenameField(
model_name="propertymapping", old_name="template", new_name="expression",
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 3.0.3 on 2020-02-20 12:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0007_auto_20200217_1934"),
]
operations = [
migrations.RenameField(
model_name="application", old_name="icon_url", new_name="meta_icon_url",
),
migrations.RenameField(
model_name="application", old_name="launch_url", new_name="meta_launch_url",
),
migrations.AddField(
model_name="application",
name="meta_description",
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name="application",
name="meta_publisher",
field=models.TextField(blank=True, null=True),
),
]

View File

@ -0,0 +1,52 @@
# Generated by Django 3.0.3 on 2020-02-21 14:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0008_auto_20200220_1242"),
]
operations = [
migrations.AlterField(
model_name="application",
name="name",
field=models.TextField(help_text="Application's display Name."),
),
migrations.AlterField(
model_name="application",
name="slug",
field=models.SlugField(
help_text="Internal application name, used in URLs."
),
),
migrations.AlterField(
model_name="factor",
name="name",
field=models.TextField(help_text="Factor's display Name."),
),
migrations.AlterField(
model_name="factor",
name="slug",
field=models.SlugField(
help_text="Internal factor name, used in URLs.", unique=True
),
),
migrations.AlterField(
model_name="source",
name="name",
field=models.TextField(help_text="Source's display Name."),
),
migrations.AlterField(
model_name="source",
name="slug",
field=models.SlugField(help_text="Internal source name, used in URLs."),
),
migrations.AlterField(
model_name="user",
name="name",
field=models.TextField(help_text="User's display name."),
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 3.0.3 on 2020-02-21 22:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0009_auto_20200221_1410"),
]
operations = [
migrations.AlterField(
model_name="application",
name="meta_description",
field=models.TextField(blank=True, default=""),
),
migrations.AlterField(
model_name="application",
name="meta_icon_url",
field=models.TextField(blank=True, default=""),
),
migrations.AlterField(
model_name="application",
name="meta_launch_url",
field=models.URLField(blank=True, default=""),
),
migrations.AlterField(
model_name="application",
name="meta_publisher",
field=models.TextField(blank=True, default=""),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.0.3 on 2020-02-22 18:22
from django.db import migrations
def fix_application_null(apps, schema_editor):
"""Fix Application meta_fields being null"""
Application = apps.get_model("passbook_core", "Application")
for app in Application.objects.all():
if app.meta_launch_url is None:
app.meta_launch_url = ""
if app.meta_icon_url is None:
app.meta_icon_url = ""
if app.meta_description is None:
app.meta_description = ""
if app.meta_publisher is None:
app.meta_publisher = ""
app.save()
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0010_auto_20200221_2208"),
]
operations = [migrations.RunPython(fix_application_null)]

View File

@ -0,0 +1,14 @@
# Generated by Django 3.0.3 on 2020-05-08 17:58
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0011_auto_20200222_1822"),
]
operations = [
migrations.DeleteModel(name="Factor",),
]

View File

@ -0,0 +1,16 @@
# Generated by Django 3.0.5 on 2020-05-10 10:01
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_policies", "0003_auto_20200508_1642"),
("passbook_stages_password", "0001_initial"),
("passbook_core", "0012_delete_factor"),
]
operations = [
migrations.DeleteModel(name="DebugPolicy",),
]

View File

@ -0,0 +1,14 @@
# Generated by Django 3.0.5 on 2020-05-11 19:57
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0013_delete_debugpolicy"),
]
operations = [
migrations.DeleteModel(name="Invitation",),
]

View File

@ -10,6 +10,7 @@ from django.db import models
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_prometheus.models import ExportModelOperationsMixin
from guardian.mixins import GuardianUserMixin from guardian.mixins import GuardianUserMixin
from jinja2 import Undefined from jinja2 import Undefined
from jinja2.exceptions import TemplateSyntaxError, UndefinedError from jinja2.exceptions import TemplateSyntaxError, UndefinedError
@ -21,18 +22,19 @@ from passbook.core.exceptions import PropertyMappingExpressionException
from passbook.core.signals import password_changed from passbook.core.signals import password_changed
from passbook.core.types import UILoginButton, UIUserSettings from passbook.core.types import UILoginButton, UIUserSettings
from passbook.lib.models import CreatedUpdatedModel, UUIDModel from passbook.lib.models import CreatedUpdatedModel, UUIDModel
from passbook.policies.models import PolicyBindingModel from passbook.policies.exceptions import PolicyException
from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()
NATIVE_ENVIRONMENT = NativeEnvironment() NATIVE_ENVIRONMENT = NativeEnvironment()
def default_token_duration(): def default_nonce_duration():
"""Default duration a Token is valid""" """Default duration a Nonce is valid"""
return now() + timedelta(minutes=30) return now() + timedelta(minutes=30)
class Group(UUIDModel): class Group(ExportModelOperationsMixin("group"), UUIDModel):
"""Custom Group model which supports a basic hierarchy""" """Custom Group model which supports a basic hierarchy"""
name = models.CharField(_("name"), max_length=80) name = models.CharField(_("name"), max_length=80)
@ -53,13 +55,13 @@ class Group(UUIDModel):
unique_together = (("name", "parent",),) unique_together = (("name", "parent",),)
class User(GuardianUserMixin, AbstractUser): class User(ExportModelOperationsMixin("user"), GuardianUserMixin, AbstractUser):
"""Custom User model to allow easier adding o f user-based settings""" """Custom User model to allow easier adding o f user-based settings"""
uuid = models.UUIDField(default=uuid4, editable=False) uuid = models.UUIDField(default=uuid4, editable=False)
name = models.TextField(help_text=_("User's display name.")) name = models.TextField(help_text=_("User's display name."))
inlets = models.ManyToManyField("Inlet", through="UserInletConnection") sources = models.ManyToManyField("Source", through="UserSourceConnection")
groups = models.ManyToManyField("Group") groups = models.ManyToManyField("Group")
password_change_date = models.DateTimeField(auto_now_add=True) password_change_date = models.DateTimeField(auto_now_add=True)
@ -76,7 +78,29 @@ class User(GuardianUserMixin, AbstractUser):
permissions = (("reset_user_password", "Reset Password"),) permissions = (("reset_user_password", "Reset Password"),)
class Application(PolicyBindingModel): class Provider(ExportModelOperationsMixin("provider"), models.Model):
"""Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
property_mappings = models.ManyToManyField(
"PropertyMapping", default=None, blank=True
)
objects = InheritanceManager()
# This class defines no field for easier inheritance
def __str__(self):
if hasattr(self, "name"):
return getattr(self, "name")
return super().__str__()
class PolicyModel(UUIDModel, CreatedUpdatedModel):
"""Base model which can have policies applied to it"""
policies = models.ManyToManyField("Policy", blank=True)
class Application(ExportModelOperationsMixin("application"), PolicyModel):
"""Every Application which uses passbook for authentication/identification/authorization """Every Application which uses passbook for authentication/identification/authorization
needs an Application record. Other authentication types can subclass this Model to needs an Application record. Other authentication types can subclass this Model to
add custom fields and other properties""" add custom fields and other properties"""
@ -84,8 +108,8 @@ class Application(PolicyBindingModel):
name = models.TextField(help_text=_("Application's display Name.")) name = models.TextField(help_text=_("Application's display Name."))
slug = models.SlugField(help_text=_("Internal application name, used in URLs.")) slug = models.SlugField(help_text=_("Internal application name, used in URLs."))
skip_authorization = models.BooleanField(default=False) skip_authorization = models.BooleanField(default=False)
outlet = models.OneToOneField( provider = models.OneToOneField(
"Outlet", null=True, blank=True, default=None, on_delete=models.SET_DEFAULT "Provider", null=True, blank=True, default=None, on_delete=models.SET_DEFAULT
) )
meta_launch_url = models.URLField(default="", blank=True) meta_launch_url = models.URLField(default="", blank=True)
@ -95,20 +119,20 @@ class Application(PolicyBindingModel):
objects = InheritanceManager() objects = InheritanceManager()
def get_outlet(self) -> Optional["Outlet"]: def get_provider(self) -> Optional[Provider]:
"""Get casted outlet instance""" """Get casted provider instance"""
if not self.outlet: if not self.provider:
return None return None
return Outlet.objects.get_subclass(pk=self.outlet.pk) return Provider.objects.get_subclass(pk=self.provider.pk)
def __str__(self): def __str__(self):
return self.name return self.name
class Inlet(PolicyBindingModel): class Source(ExportModelOperationsMixin("source"), PolicyModel):
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server""" """Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
name = models.TextField(help_text=_("Inlet's display Name.")) name = models.TextField(help_text=_("Source's display Name."))
slug = models.SlugField(help_text=_("Internal source name, used in URLs.")) slug = models.SlugField(help_text=_("Internal source name, used in URLs."))
enabled = models.BooleanField(default=True) enabled = models.BooleanField(default=True)
@ -141,69 +165,56 @@ class Inlet(PolicyBindingModel):
return self.name return self.name
class UserInletConnection(CreatedUpdatedModel): class UserSourceConnection(CreatedUpdatedModel):
"""Connection between User and Inlet.""" """Connection between User and Source."""
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE)
inlet = models.ForeignKey(Inlet, on_delete=models.CASCADE) source = models.ForeignKey(Source, on_delete=models.CASCADE)
class Meta: class Meta:
unique_together = (("user", "inlet"),) unique_together = (("user", "source"),)
class Token(UUIDModel): class Policy(ExportModelOperationsMixin("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."""
name = models.TextField(blank=True, null=True)
negate = models.BooleanField(default=False)
order = models.IntegerField(default=0)
timeout = models.IntegerField(default=30)
objects = InheritanceManager()
def __str__(self):
return f"Policy {self.name}"
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if user instance passes this policy"""
raise PolicyException()
class Nonce(ExportModelOperationsMixin("nonce"), UUIDModel):
"""One-time link for password resets/sign-up-confirmations""" """One-time link for password resets/sign-up-confirmations"""
expires = models.DateTimeField(default=default_token_duration) expires = models.DateTimeField(default=default_nonce_duration)
user = models.ForeignKey("User", on_delete=models.CASCADE, related_name="+") user = models.ForeignKey("User", on_delete=models.CASCADE)
expiring = models.BooleanField(default=True) expiring = models.BooleanField(default=True)
description = models.TextField(default="", blank=True) description = models.TextField(default="", blank=True)
@property @property
def is_expired(self) -> bool: def is_expired(self) -> bool:
"""Check if token is expired yet.""" """Check if nonce is expired yet."""
return now() > self.expires return now() > self.expires
def __str__(self): def __str__(self):
return f"Token f{self.uuid.hex} {self.description} (expires={self.expires})" return f"Nonce f{self.uuid.hex} {self.description} (expires={self.expires})"
class Meta: class Meta:
verbose_name = _("Token") verbose_name = _("Nonce")
verbose_name_plural = _("Tokens") verbose_name_plural = _("Nonces")
class Outlet(models.Model):
"""Application-independent Outlet instance. For example SAML2 Remote, OAuth2 Application"""
property_mappings = models.ManyToManyField(
"PropertyMapping", default=None, blank=True
)
objects = InheritanceManager()
# This class defines no field for easier inheritance
def __str__(self):
if hasattr(self, "name"):
return getattr(self, "name")
return super().__str__()
class PropertyMappingBinding(UUIDModel):
"""Binds a PropertyMapping instance to an outlet"""
property_mapping = models.ForeignKey("PropertyMapping", on_delete=models.CASCADE)
outlet = models.ForeignKey("Outlet", on_delete=models.CASCADE)
order = models.IntegerField(default=0)
def __str__(self):
return f"PropertyMapping Binding p={self.property_mapping} outlet={self.outlet}"
class Meta:
unique_together = (("property_mapping", "outlet", "order"),)
class PropertyMapping(UUIDModel): class PropertyMapping(UUIDModel):

View File

@ -17,7 +17,7 @@ password_changed = Signal(providing_args=["user", "password"])
# pylint: disable=unused-argument # pylint: disable=unused-argument
def invalidate_policy_cache(sender, instance, **_): def invalidate_policy_cache(sender, instance, **_):
"""Invalidate Policy cache when policy is updated""" """Invalidate Policy cache when policy is updated"""
from passbook.policies.models import Policy from passbook.core.models import Policy
from passbook.policies.process import cache_key from passbook.policies.process import cache_key
if isinstance(instance, Policy): if isinstance(instance, Policy):

View File

@ -2,14 +2,14 @@
from django.utils.timezone import now from django.utils.timezone import now
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Token from passbook.core.models import Nonce
from passbook.root.celery import CELERY_APP from passbook.root.celery import CELERY_APP
LOGGER = get_logger() LOGGER = get_logger()
@CELERY_APP.task() @CELERY_APP.task()
def clean_tokens(): def clean_nonces():
"""Remove expired tokens""" """Remove expired nonces"""
amount, _ = Token.objects.filter(expires__lt=now(), expiring=True).delete() amount, _ = Nonce.objects.filter(expires__lt=now(), expiring=True).delete()
LOGGER.debug("Deleted expired tokens", amount=amount) LOGGER.debug("Deleted expired nonces", amount=amount)

View File

@ -34,17 +34,17 @@
</ul> </ul>
</section> </section>
{% endif %} {% endif %}
{% user_inlets as user_inlets_loc %} {% user_sources as user_sources_loc %}
{% if user_inlets_loc %} {% if user_sources_loc %}
<section class="pf-c-nav__section"> <section class="pf-c-nav__section">
<h2 class="pf-c-nav__section-title">{% trans 'Sources' %}</h2> <h2 class="pf-c-nav__section-title">{% trans 'Sources' %}</h2>
<ul class="pf-c-nav__list"> <ul class="pf-c-nav__list">
{% for inlet in user_inlets_loc %} {% for source in user_sources_loc %}
<li class="pf-c-nav__item"> <li class="pf-c-nav__item">
<a href="{{ inlet.view_name }}" <a href="{{ source.view_name }}"
class="pf-c-nav__link {% if user_settings.view_name == request.get_full_path %} pf-m-current {% endif %}"> class="pf-c-nav__link {% if user_settings.view_name == request.get_full_path %} pf-m-current {% endif %}">
<i class="{{ inlet.icon }}"></i> <i class="{{ source.icon }}"></i>
{{ inlet.name }} {{ source.name }}
</a> </a>
</li> </li>
{% endfor %} {% endfor %}

View File

@ -4,7 +4,7 @@ from typing import Iterable, List
from django import template from django import template
from django.template.context import RequestContext from django.template.context import RequestContext
from passbook.core.models import Inlet from passbook.core.models import Source
from passbook.core.types import UIUserSettings from passbook.core.types import UIUserSettings
from passbook.flows.models import Stage from passbook.flows.models import Stage
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
@ -27,14 +27,14 @@ def user_stages(context: RequestContext) -> List[UIUserSettings]:
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
def user_inlets(context: RequestContext) -> List[UIUserSettings]: def user_sources(context: RequestContext) -> List[UIUserSettings]:
"""Return a list of all inlets which are enabled for the user""" """Return a list of all sources which are enabled for the user"""
user = context.get("request").user user = context.get("request").user
_all_inlets: Iterable[(Inlet)] = ( _all_sources: Iterable[Source] = (
(Inlet).objects.filter(enabled=True).select_subclasses() Source.objects.filter(enabled=True).select_subclasses()
) )
matching_inlets: List[UIUserSettings] = [] matching_sources: List[UIUserSettings] = []
for source in _all_inlets: for source in _all_sources:
user_settings = source.ui_user_settings user_settings = source.ui_user_settings
if not user_settings: if not user_settings:
continue continue
@ -43,5 +43,5 @@ def user_inlets(context: RequestContext) -> List[UIUserSettings]:
) )
policy_engine.build() policy_engine.build()
if policy_engine.passing: if policy_engine.passing:
matching_inlets.append(user_settings) matching_sources.append(user_settings)
return matching_inlets return matching_sources

View File

@ -6,7 +6,7 @@ from django.http import HttpRequest
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Application, Outlet, User from passbook.core.models import Application, Provider, User
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
LOGGER = get_logger() LOGGER = get_logger()
@ -14,19 +14,22 @@ LOGGER = get_logger()
class AccessMixin: class AccessMixin:
"""Mixin class for usage in Authorization views. """Mixin class for usage in Authorization views.
Outlet functions to check application access, etc""" Provider functions to check application access, etc"""
# request is set by view but since this Mixin has no base class # request is set by view but since this Mixin has no base class
request: HttpRequest = None request: HttpRequest = None
def outlet_to_application(self, outlet: Outlet) -> Application: def provider_to_application(self, provider: Provider) -> Application:
"""Lookup application assigned to outlet, throw error if no application assigned""" """Lookup application assigned to provider, throw error if no application assigned"""
try: try:
return outlet.application return provider.application
except Application.DoesNotExist as exc: except Application.DoesNotExist as exc:
messages.error( messages.error(
self.request, self.request,
_('Outlet "%(name)s" has no application assigned' % {"name": outlet}), _(
'Provider "%(name)s" has no application assigned'
% {"name": provider}
),
) )
raise exc raise exc

View File

@ -1,10 +1,24 @@
# Generated by Django 3.0.5 on 2020-05-15 19:58 # Generated by Django 3.0.3 on 2020-03-03 21:45
import uuid import uuid
from django.db import migrations, models from django.db import migrations, models
def create_self_signed(apps, schema_editor):
CertificateKeyPair = apps.get_model("passbook_crypto", "CertificateKeyPair")
db_alias = schema_editor.connection.alias
from passbook.crypto.builder import CertificateBuilder
builder = CertificateBuilder()
builder.build()
CertificateKeyPair.objects.using(db_alias).create(
name="passbook Self-signed Certificate",
certificate_data=builder.certificate,
key_data=builder.private_key,
)
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
@ -27,22 +41,27 @@ class Migration(migrations.Migration):
), ),
), ),
("name", models.TextField()), ("name", models.TextField()),
( ("certificate_data", models.TextField()),
"certificate_data", ("key_data", models.TextField(blank=True, default="")),
models.TextField(help_text="PEM-encoded Certificate data"),
),
(
"key_data",
models.TextField(
blank=True,
default="",
help_text="Optional Private Key. If this is set, you can use this keypair for encryption.",
),
),
], ],
options={ options={
"verbose_name": "Certificate-Key Pair", "verbose_name": "Certificate-Key Pair",
"verbose_name_plural": "Certificate-Key Pairs", "verbose_name_plural": "Certificate-Key Pairs",
}, },
), ),
migrations.RunPython(create_self_signed),
migrations.AlterField(
model_name="certificatekeypair",
name="certificate_data",
field=models.TextField(help_text="PEM-encoded Certificate data"),
),
migrations.AlterField(
model_name="certificatekeypair",
name="key_data",
field=models.TextField(
blank=True,
default="",
help_text="Optional Private Key. If this is set, you can use this keypair for encryption.",
),
),
] ]

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:58 # Generated by Django 3.0.3 on 2020-05-08 18:27
import uuid import uuid
@ -11,7 +11,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "__first__"), ("passbook_policies", "0003_auto_20200508_1642"),
] ]
operations = [ operations = [
@ -33,12 +33,10 @@ class Migration(migrations.Migration):
"designation", "designation",
models.CharField( models.CharField(
choices=[ choices=[
("authentication", "Authentication"), ("AUTHENTICATION", "authentication"),
("invalidation", "Invalidation"), ("ENROLLMENT", "enrollment"),
("enrollment", "Enrollment"), ("RECOVERY", "recovery"),
("unenrollment", "Unrenollment"), ("PASSWORD_CHANGE", "password_change"),
("recovery", "Recovery"),
("password_change", "Password Change"),
], ],
max_length=100, max_length=100,
), ),

View File

@ -0,0 +1,26 @@
# Generated by Django 3.0.3 on 2020-05-09 12:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_flows", "0002_default_flows"),
]
operations = [
migrations.AlterField(
model_name="flow",
name="designation",
field=models.CharField(
choices=[
("authentication", "Authentication"),
("enrollment", "Enrollment"),
("recovery", "Recovery"),
("password_change", "Password Change"),
],
max_length=100,
),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.0.5 on 2020-05-10 23:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_flows", "0003_auto_20200509_1258"),
]
operations = [
migrations.AlterField(
model_name="flow",
name="designation",
field=models.CharField(
choices=[
("authentication", "Authentication"),
("enrollment", "Enrollment"),
("recovery", "Recovery"),
("password_change", "Password Change"),
("invalidation", "Invalidation"),
],
max_length=100,
),
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.0.5 on 2020-05-12 11:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_flows", "0004_auto_20200510_2310"),
]
operations = [
migrations.AlterField(
model_name="flow",
name="designation",
field=models.CharField(
choices=[
("authentication", "Authentication"),
("invalidation", "Invalidation"),
("enrollment", "Enrollment"),
("unenrollment", "Unrenollment"),
("recovery", "Recovery"),
("password_change", "Password Change"),
],
max_length=100,
),
),
]

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:58 # Generated by Django 3.0.5 on 2020-05-10 10:01
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -9,7 +9,8 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "0001_initial"), ("passbook_policies", "0003_auto_20200508_1642"),
("passbook_core", "0013_delete_debugpolicy"),
] ]
operations = [ operations = [
@ -24,7 +25,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_policies.Policy", to="passbook_core.Policy",
), ),
), ),
("result", models.BooleanField(default=False)), ("result", models.BooleanField(default=False)),
@ -35,6 +36,6 @@ class Migration(migrations.Migration):
"verbose_name": "Dummy Policy", "verbose_name": "Dummy Policy",
"verbose_name_plural": "Dummy Policies", "verbose_name_plural": "Dummy Policies",
}, },
bases=("passbook_policies.policy",), bases=("passbook_core.policy",),
), ),
] ]

View File

@ -6,7 +6,7 @@ from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from structlog import get_logger from structlog import get_logger
from passbook.policies.models import Policy from passbook.core.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View File

@ -7,8 +7,7 @@ from django.core.cache import cache
from django.http import HttpRequest from django.http import HttpRequest
from structlog import get_logger from structlog import get_logger
from passbook.core.models import User from passbook.core.models import Policy, User
from passbook.policies.models import Policy
from passbook.policies.process import PolicyProcess, cache_key from passbook.policies.process import PolicyProcess, cache_key
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:58 # Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "0001_initial"), ("passbook_core", "0001_initial"),
] ]
operations = [ operations = [
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_policies.Policy", to="passbook_core.Policy",
), ),
), ),
("deny_only", models.BooleanField(default=False)), ("deny_only", models.BooleanField(default=False)),
@ -34,6 +34,6 @@ class Migration(migrations.Migration):
"verbose_name": "Password Expiry Policy", "verbose_name": "Password Expiry Policy",
"verbose_name_plural": "Password Expiry Policies", "verbose_name_plural": "Password Expiry Policies",
}, },
bases=("passbook_policies.policy",), bases=("passbook_core.policy",),
), ),
] ]

View File

@ -6,7 +6,7 @@ from django.utils.timezone import now
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from structlog import get_logger from structlog import get_logger
from passbook.policies.models import Policy from passbook.core.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59 # Generated by Django 3.0.3 on 2020-02-18 14:00
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "0001_initial"), ("passbook_core", "0007_auto_20200217_1934"),
] ]
operations = [ operations = [
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_policies.Policy", to="passbook_core.Policy",
), ),
), ),
("expression", models.TextField()), ("expression", models.TextField()),
@ -33,6 +33,6 @@ class Migration(migrations.Migration):
"verbose_name": "Expression Policy", "verbose_name": "Expression Policy",
"verbose_name_plural": "Expression Policies", "verbose_name_plural": "Expression Policies",
}, },
bases=("passbook_policies.policy",), bases=("passbook_core.policy",),
), ),
] ]

View File

@ -2,8 +2,8 @@
from django.db import models from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from passbook.core.models import Policy
from passbook.policies.expression.evaluator import Evaluator from passbook.policies.expression.evaluator import Evaluator
from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59 # Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "0001_initial"), ("passbook_core", "0001_initial"),
] ]
operations = [ operations = [
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_policies.Policy", to="passbook_core.Policy",
), ),
), ),
("allowed_count", models.IntegerField(default=0)), ("allowed_count", models.IntegerField(default=0)),
@ -33,6 +33,6 @@ class Migration(migrations.Migration):
"verbose_name": "Have I Been Pwned Policy", "verbose_name": "Have I Been Pwned Policy",
"verbose_name_plural": "Have I Been Pwned Policies", "verbose_name_plural": "Have I Been Pwned Policies",
}, },
bases=("passbook_policies.policy",), bases=("passbook_core.policy",),
), ),
] ]

View File

@ -6,8 +6,7 @@ from django.utils.translation import gettext as _
from requests import get from requests import get
from structlog import get_logger from structlog import get_logger
from passbook.policies.models import Policy from passbook.core.models import Policy, PolicyResult, User
from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()
@ -20,14 +19,14 @@ class HaveIBeenPwendPolicy(Policy):
form = "passbook.policies.hibp.forms.HaveIBeenPwnedPolicyForm" form = "passbook.policies.hibp.forms.HaveIBeenPwnedPolicyForm"
def passes(self, request: PolicyRequest) -> PolicyResult: def passes(self, user: User) -> PolicyResult:
"""Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5 """Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5
characters of Password in request and checks if full hash is in response. Returns 0 characters of Password in request and checks if full hash is in response. Returns 0
if Password is not in result otherwise the count of how many times it was used.""" if Password is not in result otherwise the count of how many times it was used."""
# Only check if password is being set # Only check if password is being set
if not hasattr(request.user, "__password__"): if not hasattr(user, "__password__"):
return PolicyResult(True) return PolicyResult(True)
password = getattr(request.user, "__password__") password = getattr(user, "__password__")
pw_hash = sha1(password.encode("utf-8")).hexdigest() # nosec pw_hash = sha1(password.encode("utf-8")).hexdigest() # nosec
url = "https://api.pwnedpasswords.com/range/%s" % pw_hash[:5] url = "https://api.pwnedpasswords.com/range/%s" % pw_hash[:5]
result = get(url).text result = get(url).text

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:58 # Generated by Django 3.0.3 on 2020-05-07 18:35
import uuid import uuid
@ -10,30 +10,11 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [] dependencies = [
("passbook_core", "0011_auto_20200222_1822"),
]
operations = [ operations = [
migrations.CreateModel(
name="Policy",
fields=[
("created", models.DateTimeField(auto_now_add=True)),
("last_updated", models.DateTimeField(auto_now=True)),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("name", models.TextField(blank=True, null=True)),
("negate", models.BooleanField(default=False)),
("order", models.IntegerField(default=0)),
("timeout", models.IntegerField(default=30)),
],
options={"abstract": False,},
),
migrations.CreateModel( migrations.CreateModel(
name="PolicyBinding", name="PolicyBinding",
fields=[ fields=[
@ -53,7 +34,7 @@ class Migration(migrations.Migration):
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
related_name="+", related_name="+",
to="passbook_policies.Policy", to="passbook_core.Policy",
), ),
), ),
], ],
@ -77,17 +58,12 @@ class Migration(migrations.Migration):
( (
"policies", "policies",
models.ManyToManyField( models.ManyToManyField(
blank=True,
related_name="_policybindingmodel_policies_+", related_name="_policybindingmodel_policies_+",
through="passbook_policies.PolicyBinding", through="passbook_policies.PolicyBinding",
to="passbook_policies.Policy", to="passbook_core.Policy",
), ),
), ),
], ],
options={
"verbose_name": "Policy Binding Model",
"verbose_name_plural": "Policy Binding Models",
},
), ),
migrations.AddField( migrations.AddField(
model_name="policybinding", model_name="policybinding",

View File

@ -0,0 +1,20 @@
# Generated by Django 3.0.3 on 2020-05-08 12:30
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_policies", "0001_initial"),
]
operations = [
migrations.AlterModelOptions(
name="policybindingmodel",
options={
"verbose_name": "Policy Binding Model",
"verbose_name_plural": "Policy Binding Models",
},
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 3.0.3 on 2020-05-08 16:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0011_auto_20200222_1822"),
("passbook_policies", "0002_auto_20200508_1230"),
]
operations = [
migrations.AlterField(
model_name="policybindingmodel",
name="policies",
field=models.ManyToManyField(
blank=True,
related_name="_policybindingmodel_policies_+",
through="passbook_policies.PolicyBinding",
to="passbook_core.Policy",
),
),
]

View File

@ -1,30 +1,9 @@
"""Policy base models""" """Policy base models"""
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager
from passbook.lib.models import CreatedUpdatedModel, UUIDModel from passbook.core.models import Policy
from passbook.policies.exceptions import PolicyException from passbook.lib.models import UUIDModel
from passbook.policies.types import PolicyRequest, PolicyResult
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."""
name = models.TextField(blank=True, null=True)
negate = models.BooleanField(default=False)
order = models.IntegerField(default=0)
timeout = models.IntegerField(default=30)
objects = InheritanceManager()
def __str__(self):
return f"Policy {self.name}"
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if user instance passes this policy"""
raise PolicyException()
class PolicyBindingModel(models.Model): class PolicyBindingModel(models.Model):

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59 # Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "0001_initial"), ("passbook_core", "0001_initial"),
] ]
operations = [ operations = [
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_policies.Policy", to="passbook_core.Policy",
), ),
), ),
("amount_uppercase", models.IntegerField(default=0)), ("amount_uppercase", models.IntegerField(default=0)),
@ -41,6 +41,6 @@ class Migration(migrations.Migration):
"verbose_name": "Password Policy", "verbose_name": "Password Policy",
"verbose_name_plural": "Password Policies", "verbose_name_plural": "Password Policies",
}, },
bases=("passbook_policies.policy",), bases=("passbook_core.policy",),
), ),
] ]

View File

@ -5,7 +5,7 @@ from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from structlog import get_logger from structlog import get_logger
from passbook.policies.models import Policy from passbook.core.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View File

@ -6,9 +6,8 @@ from typing import Optional
from django.core.cache import cache from django.core.cache import cache
from structlog import get_logger from structlog import get_logger
from passbook.core.models import User from passbook.core.models import Policy, User
from passbook.policies.exceptions import PolicyException from passbook.policies.exceptions import PolicyException
from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59 # Generated by Django 2.2.6 on 2019-10-07 14:07
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -10,7 +10,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_policies", "0001_initial"), ("passbook_core", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
@ -43,7 +43,7 @@ class Migration(migrations.Migration):
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_policies.Policy", to="passbook_core.Policy",
), ),
), ),
("check_ip", models.BooleanField(default=True)), ("check_ip", models.BooleanField(default=True)),
@ -54,7 +54,7 @@ class Migration(migrations.Migration):
"verbose_name": "Reputation Policy", "verbose_name": "Reputation Policy",
"verbose_name_plural": "Reputation Policies", "verbose_name_plural": "Reputation Policies",
}, },
bases=("passbook_policies.policy",), bases=("passbook_core.policy",),
), ),
migrations.CreateModel( migrations.CreateModel(
name="UserReputation", name="UserReputation",

View File

@ -2,9 +2,8 @@
from django.db import models from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from passbook.core.models import User from passbook.core.models import Policy, User
from passbook.lib.utils.http import get_client_ip from passbook.lib.utils.http import get_client_ip
from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult from passbook.policies.types import PolicyRequest, PolicyResult

View File

@ -2,10 +2,9 @@
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase from django.test import TestCase
from passbook.core.models import User from passbook.core.models import Policy, User
from passbook.policies.dummy.models import DummyPolicy from passbook.policies.dummy.models import DummyPolicy
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
from passbook.policies.models import Policy
class PolicyTestEngine(TestCase): class PolicyTestEngine(TestCase):

View File

@ -1,17 +1,17 @@
"""ApplicationGatewayOutlet API Views""" """ApplicationGatewayProvider API Views"""
from oauth2_provider.generators import generate_client_id, generate_client_secret from oauth2_provider.generators import generate_client_id, generate_client_secret
from oidc_provider.models import Client from oidc_provider.models import Client
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from passbook.channels.out_app_gw.models import ApplicationGatewayOutlet from passbook.providers.app_gw.models import ApplicationGatewayProvider
from passbook.channels.out_oidc.api import OpenIDOutletSerializer from passbook.providers.oidc.api import OpenIDProviderSerializer
class ApplicationGatewayOutletSerializer(ModelSerializer): class ApplicationGatewayProviderSerializer(ModelSerializer):
"""ApplicationGatewayOutlet Serializer""" """ApplicationGatewayProvider Serializer"""
client = OpenIDOutletSerializer() client = OpenIDProviderSerializer()
def create(self, validated_data): def create(self, validated_data):
instance = super().create(validated_data) instance = super().create(validated_data)
@ -33,13 +33,13 @@ class ApplicationGatewayOutletSerializer(ModelSerializer):
class Meta: class Meta:
model = ApplicationGatewayOutlet model = ApplicationGatewayProvider
fields = ["pk", "name", "internal_host", "external_host", "client"] fields = ["pk", "name", "internal_host", "external_host", "client"]
read_only_fields = ["client"] read_only_fields = ["client"]
class ApplicationGatewayOutletViewSet(ModelViewSet): class ApplicationGatewayProviderViewSet(ModelViewSet):
"""ApplicationGatewayOutlet Viewset""" """ApplicationGatewayProvider Viewset"""
queryset = ApplicationGatewayOutlet.objects.all() queryset = ApplicationGatewayProvider.objects.all()
serializer_class = ApplicationGatewayOutletSerializer serializer_class = ApplicationGatewayProviderSerializer

View File

@ -5,7 +5,7 @@ from django.apps import AppConfig
class PassbookApplicationApplicationGatewayConfig(AppConfig): class PassbookApplicationApplicationGatewayConfig(AppConfig):
"""passbook app_gw app""" """passbook app_gw app"""
name = "passbook.channels.out_app_gw" name = "passbook.providers.app_gw"
label = "passbook_channels_out_app_gw" label = "passbook_providers_app_gw"
verbose_name = "passbook Outlets.Application Security Gateway" verbose_name = "passbook Providers.Application Security Gateway"
mountpoint = "application/gateway/" mountpoint = "application/gateway/"

View File

@ -3,11 +3,11 @@ from django import forms
from oauth2_provider.generators import generate_client_id, generate_client_secret from oauth2_provider.generators import generate_client_id, generate_client_secret
from oidc_provider.models import Client, ResponseType from oidc_provider.models import Client, ResponseType
from passbook.channels.out_app_gw.models import ApplicationGatewayOutlet from passbook.providers.app_gw.models import ApplicationGatewayProvider
class ApplicationGatewayOutletForm(forms.ModelForm): class ApplicationGatewayProviderForm(forms.ModelForm):
"""Security Gateway Outlet form""" """Security Gateway Provider form"""
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.instance.pk: if not self.instance.pk:
@ -31,7 +31,7 @@ class ApplicationGatewayOutletForm(forms.ModelForm):
class Meta: class Meta:
model = ApplicationGatewayOutlet model = ApplicationGatewayProvider
fields = ["name", "internal_host", "external_host"] fields = ["name", "internal_host", "external_host"]
widgets = { widgets = {
"name": forms.TextInput(), "name": forms.TextInput(),

View File

@ -0,0 +1,99 @@
# 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="ApplicationGatewayProvider",
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",
),
),
(
"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(blank=True, default="X-Remote-User"),
),
(
"default_content_type",
models.TextField(default="application/octet-stream"),
),
("upstream_ssl_verification", models.BooleanField(default=True)),
],
options={
"verbose_name": "Application Gateway Provider",
"verbose_name_plural": "Application Gateway Providers",
},
bases=("passbook_core.provider",),
),
migrations.CreateModel(
name="RewriteRule",
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",
),
),
("match", models.TextField()),
("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(blank=True, to="passbook_core.Policy"),
),
],
options={
"verbose_name": "Rewrite Rule",
"verbose_name_plural": "Rewrite Rules",
},
bases=("passbook_core.propertymapping",),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.7 on 2019-11-11 17:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0005_merge_20191025_2022"),
("passbook_providers_app_gw", "0001_initial"),
]
operations = [
migrations.RemoveField(model_name="rewriterule", name="conditions",),
migrations.RemoveField(model_name="rewriterule", name="propertymapping_ptr",),
migrations.DeleteModel(name="ApplicationGatewayProvider",),
migrations.DeleteModel(name="RewriteRule",),
]

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2020-05-15 19:59 # Generated by Django 2.2.7 on 2019-11-11 17:08
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -9,28 +9,28 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("passbook_core", "__first__"), ("passbook_core", "0005_merge_20191025_2022"),
("oidc_provider", "0026_client_multiple_response_types"), ("oidc_provider", "0026_client_multiple_response_types"),
("passbook_providers_app_gw", "0002_auto_20191111_1703"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name="ApplicationGatewayOutlet", name="ApplicationGatewayProvider",
fields=[ fields=[
( (
"outlet_ptr", "provider_ptr",
models.OneToOneField( models.OneToOneField(
auto_created=True, auto_created=True,
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
parent_link=True, parent_link=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
to="passbook_core.Outlet", to="passbook_core.Provider",
), ),
), ),
("name", models.TextField()), ("name", models.TextField()),
("internal_host", models.TextField()), ("host", models.TextField()),
("external_host", models.TextField()),
( (
"client", "client",
models.ForeignKey( models.ForeignKey(
@ -40,9 +40,9 @@ class Migration(migrations.Migration):
), ),
], ],
options={ options={
"verbose_name": "Application Gateway Outlet", "verbose_name": "Application Gateway Provider",
"verbose_name_plural": "Application Gateway Outlets", "verbose_name_plural": "Application Gateway Providers",
}, },
bases=("passbook_core.outlet",), bases=("passbook_core.provider",),
), ),
] ]

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.9 on 2020-01-02 15:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_providers_app_gw", "0003_applicationgatewayprovider"),
]
operations = [
migrations.RenameField(
model_name="applicationgatewayprovider",
old_name="host",
new_name="external_host",
),
migrations.AddField(
model_name="applicationgatewayprovider",
name="internal_host",
field=models.TextField(default=""),
preserve_default=False,
),
]

View File

@ -9,12 +9,12 @@ from django.utils.translation import gettext as _
from oidc_provider.models import Client from oidc_provider.models import Client
from passbook import __version__ from passbook import __version__
from passbook.core.models import Outlet from passbook.core.models import Provider
from passbook.lib.utils.template import render_to_string from passbook.lib.utils.template import render_to_string
class ApplicationGatewayOutlet(Outlet): class ApplicationGatewayProvider(Provider):
"""This outlet uses oauth2_proxy with the OIDC Outlet.""" """This provider uses oauth2_proxy with the OIDC Provider."""
name = models.TextField() name = models.TextField()
internal_host = models.TextField() internal_host = models.TextField()
@ -22,7 +22,7 @@ class ApplicationGatewayOutlet(Outlet):
client = models.ForeignKey(Client, on_delete=models.CASCADE) client = models.ForeignKey(Client, on_delete=models.CASCADE)
form = "passbook.channels.out_app_gw.forms.ApplicationGatewayOutletForm" form = "passbook.providers.app_gw.forms.ApplicationGatewayProviderForm"
def html_setup_urls(self, request: HttpRequest) -> Optional[str]: def html_setup_urls(self, request: HttpRequest) -> Optional[str]:
"""return template and context modal with URLs for authorize, token, openid-config, etc""" """return template and context modal with URLs for authorize, token, openid-config, etc"""
@ -32,7 +32,7 @@ class ApplicationGatewayOutlet(Outlet):
) )
return render_to_string( return render_to_string(
"app_gw/setup_modal.html", "app_gw/setup_modal.html",
{"outlet": self, "cookie_secret": cookie_secret, "version": __version__}, {"provider": self, "cookie_secret": cookie_secret, "version": __version__},
) )
def __str__(self): def __str__(self):
@ -40,5 +40,5 @@ class ApplicationGatewayOutlet(Outlet):
class Meta: class Meta:
verbose_name = _("Application Gateway Outlet") verbose_name = _("Application Gateway Provider")
verbose_name_plural = _("Application Gateway Outlets") verbose_name_plural = _("Application Gateway Providers")

Some files were not shown because too many files have changed in this diff Show More