Revert "*: providers and sources -> channels, PolicyModel to PolicyBindingModel that uses custom M2M through"
This reverts commit 7ed3ceb960
.
This commit is contained in:
parent
7ed3ceb960
commit
406f69080b
|
@ -1,4 +0,0 @@
|
|||
"""passbook core inlet form fields"""
|
||||
|
||||
INLET_FORM_FIELDS = ["name", "slug", "enabled"]
|
||||
INLET_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled"]
|
|
@ -0,0 +1,4 @@
|
|||
"""passbook core source form fields"""
|
||||
|
||||
SOURCE_FORM_FIELDS = ["name", "slug", "enabled"]
|
||||
SOURCE_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled"]
|
|
@ -8,12 +8,12 @@ from passbook.admin.views import (
|
|||
debug,
|
||||
flows,
|
||||
groups,
|
||||
inlets,
|
||||
invitations,
|
||||
outlets,
|
||||
overview,
|
||||
policies,
|
||||
policy,
|
||||
property_mapping,
|
||||
providers,
|
||||
sources,
|
||||
stages,
|
||||
users,
|
||||
)
|
||||
|
@ -39,49 +39,51 @@ urlpatterns = [
|
|||
applications.ApplicationDeleteView.as_view(),
|
||||
name="application-delete",
|
||||
),
|
||||
# Inlets
|
||||
path("inlets/", inlets.InletListView.as_view(), name="inlets"),
|
||||
path("inlets/create/", inlets.InletCreateView.as_view(), name="inlet-create"),
|
||||
# Sources
|
||||
path("sources/", sources.SourceListView.as_view(), name="sources"),
|
||||
path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
|
||||
path(
|
||||
"inlets/<uuid:pk>/update/",
|
||||
inlets.InletUpdateView.as_view(),
|
||||
name="inlet-update",
|
||||
"sources/<uuid:pk>/update/",
|
||||
sources.SourceUpdateView.as_view(),
|
||||
name="source-update",
|
||||
),
|
||||
path(
|
||||
"inlets/<uuid:pk>/delete/",
|
||||
inlets.InletDeleteView.as_view(),
|
||||
name="inlet-delete",
|
||||
"sources/<uuid:pk>/delete/",
|
||||
sources.SourceDeleteView.as_view(),
|
||||
name="source-delete",
|
||||
),
|
||||
# Policies
|
||||
path("policies/", policies.PolicyListView.as_view(), name="policies"),
|
||||
path("policies/create/", policies.PolicyCreateView.as_view(), name="policy-create"),
|
||||
path("policies/", policy.PolicyListView.as_view(), name="policies"),
|
||||
path("policies/create/", policy.PolicyCreateView.as_view(), name="policy-create"),
|
||||
path(
|
||||
"policies/<uuid:pk>/update/",
|
||||
policies.PolicyUpdateView.as_view(),
|
||||
policy.PolicyUpdateView.as_view(),
|
||||
name="policy-update",
|
||||
),
|
||||
path(
|
||||
"policies/<uuid:pk>/delete/",
|
||||
policies.PolicyDeleteView.as_view(),
|
||||
policy.PolicyDeleteView.as_view(),
|
||||
name="policy-delete",
|
||||
),
|
||||
path(
|
||||
"policies/<uuid:pk>/test/",
|
||||
policies.PolicyTestView.as_view(),
|
||||
name="policy-test",
|
||||
"policies/<uuid:pk>/test/", policy.PolicyTestView.as_view(), name="policy-test"
|
||||
),
|
||||
# Outlets
|
||||
path("outlets/", outlets.OutletListView.as_view(), name="outlets"),
|
||||
path("outlets/create/", outlets.OutletCreateView.as_view(), name="outlet-create",),
|
||||
# Providers
|
||||
path("providers/", providers.ProviderListView.as_view(), name="providers"),
|
||||
path(
|
||||
"outlets/<int:pk>/update/",
|
||||
outlets.OutletUpdateView.as_view(),
|
||||
name="outlet-update",
|
||||
"providers/create/",
|
||||
providers.ProviderCreateView.as_view(),
|
||||
name="provider-create",
|
||||
),
|
||||
path(
|
||||
"outlets/<int:pk>/delete/",
|
||||
outlets.OutletDeleteView.as_view(),
|
||||
name="outlet-delete",
|
||||
"providers/<int:pk>/update/",
|
||||
providers.ProviderUpdateView.as_view(),
|
||||
name="provider-update",
|
||||
),
|
||||
path(
|
||||
"providers/<int:pk>/delete/",
|
||||
providers.ProviderDeleteView.as_view(),
|
||||
name="provider-delete",
|
||||
),
|
||||
# Stages
|
||||
path("stages/", stages.StageListView.as_view(), name="stages"),
|
||||
|
|
|
@ -5,9 +5,8 @@ from django.views.generic import TemplateView
|
|||
|
||||
from passbook import __version__
|
||||
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.policies.models import Policy
|
||||
from passbook.root.celery import CELERY_APP
|
||||
from passbook.stages.invitation.models import Invitation
|
||||
|
||||
|
@ -28,14 +27,16 @@ class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
|
|||
kwargs["application_count"] = len(Application.objects.all())
|
||||
kwargs["policy_count"] = len(Policy.objects.all())
|
||||
kwargs["user_count"] = len(User.objects.all())
|
||||
kwargs["outlet_count"] = len(Outlet.objects.all())
|
||||
kwargs["inlet_count"] = len(Inlet.objects.all())
|
||||
kwargs["provider_count"] = len(Provider.objects.all())
|
||||
kwargs["source_count"] = len(Source.objects.all())
|
||||
kwargs["stage_count"] = len(Stage.objects.all())
|
||||
kwargs["flow_count"] = len(Flow.objects.all())
|
||||
kwargs["invitation_count"] = len(Invitation.objects.all())
|
||||
kwargs["version"] = __version__
|
||||
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(
|
||||
Policy.objects.filter(policymodel__isnull=True)
|
||||
)
|
||||
|
|
|
@ -13,10 +13,10 @@ from django.views.generic.detail import DetailView
|
|||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
|
||||
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.views import CreateAssignPermView
|
||||
from passbook.policies.engine import PolicyEngine
|
||||
from passbook.policies.models import Policy
|
||||
|
||||
|
||||
class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|
@ -1,4 +1,4 @@
|
|||
"""passbook Inlet administration"""
|
||||
"""passbook Provider administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
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 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.views import CreateAssignPermView
|
||||
|
||||
|
||||
class InletListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
"""Show list of all inlets"""
|
||||
class ProviderListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
"""Show list of all providers"""
|
||||
|
||||
model = Inlet
|
||||
permission_required = "passbook_core.view_inlet"
|
||||
ordering = "name"
|
||||
paginate_by = 40
|
||||
template_name = "administration/inlet/list.html"
|
||||
model = Provider
|
||||
permission_required = "passbook_core.add_provider"
|
||||
template_name = "administration/provider/list.html"
|
||||
paginate_by = 10
|
||||
ordering = "id"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
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)
|
||||
|
||||
|
@ -35,40 +35,40 @@ class InletListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class InletCreateView(
|
||||
class ProviderCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Inlet"""
|
||||
"""Create new Provider"""
|
||||
|
||||
model = Inlet
|
||||
permission_required = "passbook_core.add_inlet"
|
||||
model = Provider
|
||||
permission_required = "passbook_core.add_provider"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:inlets")
|
||||
success_message = _("Successfully created Inlet")
|
||||
success_url = reverse_lazy("passbook_admin:providers")
|
||||
success_message = _("Successfully created Provider")
|
||||
|
||||
def get_form_class(self):
|
||||
inlet_type = self.request.GET.get("type")
|
||||
model = next(x for x in all_subclasses(Inlet) if x.__name__ == inlet_type)
|
||||
provider_type = self.request.GET.get("type")
|
||||
model = next(x for x in all_subclasses(Provider) if x.__name__ == provider_type)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
|
||||
|
||||
class InletUpdateView(
|
||||
class ProviderUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update inlet"""
|
||||
"""Update provider"""
|
||||
|
||||
model = Inlet
|
||||
permission_required = "passbook_core.change_inlet"
|
||||
model = Provider
|
||||
permission_required = "passbook_core.change_provider"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:inlets")
|
||||
success_message = _("Successfully updated Inlet")
|
||||
success_url = reverse_lazy("passbook_admin:providers")
|
||||
success_message = _("Successfully updated Provider")
|
||||
|
||||
def get_form_class(self):
|
||||
form_class_path = self.get_object().form
|
||||
|
@ -77,25 +77,29 @@ class InletUpdateView(
|
|||
|
||||
def get_object(self, queryset=None):
|
||||
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
|
||||
):
|
||||
"""Delete inlet"""
|
||||
"""Delete provider"""
|
||||
|
||||
model = Inlet
|
||||
permission_required = "passbook_core.delete_inlet"
|
||||
model = Provider
|
||||
permission_required = "passbook_core.delete_provider"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:inlets")
|
||||
success_message = _("Successfully deleted Inlet")
|
||||
success_url = reverse_lazy("passbook_admin:providers")
|
||||
success_message = _("Successfully deleted Provider")
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
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):
|
|
@ -1,4 +1,4 @@
|
|||
"""passbook Outlet administration"""
|
||||
"""passbook Source administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
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 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.views import CreateAssignPermView
|
||||
|
||||
|
||||
class OutletListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
"""Show list of all outlets"""
|
||||
class SourceListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
"""Show list of all sources"""
|
||||
|
||||
model = Outlet
|
||||
permission_required = "passbook_core.add_outlet"
|
||||
template_name = "administration/outlet/list.html"
|
||||
paginate_by = 10
|
||||
ordering = "id"
|
||||
model = Source
|
||||
permission_required = "passbook_core.view_source"
|
||||
ordering = "name"
|
||||
paginate_by = 40
|
||||
template_name = "administration/source/list.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs["types"] = {
|
||||
x.__name__: x._meta.verbose_name for x in all_subclasses(Outlet)
|
||||
x.__name__: x._meta.verbose_name for x in all_subclasses(Source)
|
||||
}
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
@ -35,40 +35,40 @@ class OutletListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
|||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class OutletCreateView(
|
||||
class SourceCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new Outlet"""
|
||||
"""Create new Source"""
|
||||
|
||||
model = Outlet
|
||||
permission_required = "passbook_core.add_outlet"
|
||||
model = Source
|
||||
permission_required = "passbook_core.add_source"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:outlets")
|
||||
success_message = _("Successfully created Outlet")
|
||||
success_url = reverse_lazy("passbook_admin:sources")
|
||||
success_message = _("Successfully created Source")
|
||||
|
||||
def get_form_class(self):
|
||||
outlet_type = self.request.GET.get("type")
|
||||
model = next(x for x in all_subclasses(Outlet) if x.__name__ == outlet_type)
|
||||
source_type = self.request.GET.get("type")
|
||||
model = next(x for x in all_subclasses(Source) if x.__name__ == source_type)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
|
||||
|
||||
class OutletUpdateView(
|
||||
class SourceUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update outlet"""
|
||||
"""Update source"""
|
||||
|
||||
model = Outlet
|
||||
permission_required = "passbook_core.change_outlet"
|
||||
model = Source
|
||||
permission_required = "passbook_core.change_source"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:outlets")
|
||||
success_message = _("Successfully updated Outlet")
|
||||
success_url = reverse_lazy("passbook_admin:sources")
|
||||
success_message = _("Successfully updated Source")
|
||||
|
||||
def get_form_class(self):
|
||||
form_class_path = self.get_object().form
|
||||
|
@ -77,25 +77,25 @@ class OutletUpdateView(
|
|||
|
||||
def get_object(self, queryset=None):
|
||||
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
|
||||
):
|
||||
"""Delete outlet"""
|
||||
"""Delete source"""
|
||||
|
||||
model = Outlet
|
||||
permission_required = "passbook_core.delete_outlet"
|
||||
model = Source
|
||||
permission_required = "passbook_core.delete_source"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:outlets")
|
||||
success_message = _("Successfully deleted Outlet")
|
||||
success_url = reverse_lazy("passbook_admin:sources")
|
||||
success_message = _("Successfully deleted Source")
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
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):
|
|
@ -16,7 +16,7 @@ from guardian.mixins import (
|
|||
)
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -92,12 +92,12 @@ class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
|
|||
permission_required = "passbook_core.reset_user_password"
|
||||
|
||||
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)
|
||||
# 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(
|
||||
reverse("passbook_flows:default-recovery", kwargs={"token": token.uuid})
|
||||
reverse("passbook_flows:default-recovery", kwargs={"nonce": nonce.uuid})
|
||||
)
|
||||
messages.success(
|
||||
request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link})
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"""permission classes for django restframework"""
|
||||
from rest_framework.permissions import BasePermission, DjangoObjectPermissions
|
||||
|
||||
from passbook.core.models import PolicyModel
|
||||
from passbook.policies.engine import PolicyEngine
|
||||
from passbook.policies.models import PolicyBindingModel
|
||||
|
||||
|
||||
class CustomObjectPermissions(DjangoObjectPermissions):
|
||||
|
@ -24,7 +24,8 @@ class PolicyPermissions(BasePermission):
|
|||
|
||||
policy_engine: PolicyEngine
|
||||
|
||||
def has_object_permission(self, request, view, obj: PolicyBindingModel) -> bool:
|
||||
self.policy_engine = PolicyEngine(obj.policies.all(), request.user, request)
|
||||
def has_object_permission(self, request, view, obj: PolicyModel) -> bool:
|
||||
# if not obj.po
|
||||
self.policy_engine = PolicyEngine(obj.policies, request.user, request)
|
||||
self.policy_engine.request.obj = obj
|
||||
return self.policy_engine.build().passing
|
||||
|
|
|
@ -9,18 +9,12 @@ from structlog import get_logger
|
|||
|
||||
from passbook.api.permissions import CustomObjectPermissions
|
||||
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.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.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.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet
|
||||
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.password.api import PasswordPolicyViewSet
|
||||
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.email.api import EmailStageViewSet
|
||||
from passbook.stages.identification.api import IdentificationStageViewSet
|
||||
|
@ -57,15 +57,9 @@ router.register("core/users", UserViewSet)
|
|||
|
||||
router.register("audit/events", EventViewSet)
|
||||
|
||||
router.register("inlets/all", InletViewSet)
|
||||
router.register("inlets/ldap", LDAPInletViewSet)
|
||||
router.register("inlets/oauth", OAuthInletViewSet)
|
||||
|
||||
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("sources/all", SourceViewSet)
|
||||
router.register("sources/ldap", LDAPSourceViewSet)
|
||||
router.register("sources/oauth", OAuthSourceViewSet)
|
||||
|
||||
router.register("policies/all", PolicyViewSet)
|
||||
router.register("policies/bindings", PolicyBindingViewSet)
|
||||
|
@ -75,6 +69,12 @@ router.register("policies/password", PasswordPolicyViewSet)
|
|||
router.register("policies/passwordexpiry", PasswordExpiryPolicyViewSet)
|
||||
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/ldap", LDAPPropertyMappingViewSet)
|
||||
router.register("propertymappings/saml", SAMLPropertyMappingViewSet)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Event",
|
||||
name="AuditEntry",
|
||||
fields=[
|
||||
(
|
||||
"uuid",
|
||||
|
@ -33,16 +33,15 @@ class Migration(migrations.Migration):
|
|||
"action",
|
||||
models.TextField(
|
||||
choices=[
|
||||
("LOGIN", "login"),
|
||||
("LOGIN_FAILED", "login_failed"),
|
||||
("LOGOUT", "logout"),
|
||||
("AUTHORIZE_APPLICATION", "authorize_application"),
|
||||
("SUSPICIOUS_REQUEST", "suspicious_request"),
|
||||
("SIGN_UP", "sign_up"),
|
||||
("PASSWORD_RESET", "password_reset"),
|
||||
("INVITE_CREATED", "invitation_created"),
|
||||
("INVITE_USED", "invitation_used"),
|
||||
("CUSTOM", "custom"),
|
||||
("login", "login"),
|
||||
("login_failed", "login_failed"),
|
||||
("logout", "logout"),
|
||||
("authorize_application", "authorize_application"),
|
||||
("suspicious_request", "suspicious_request"),
|
||||
("sign_up", "sign_up"),
|
||||
("password_reset", "password_reset"),
|
||||
("invitation_created", "invitation_created"),
|
||||
("invitation_used", "invitation_used"),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -54,7 +53,7 @@ class Migration(migrations.Migration):
|
|||
blank=True, default=dict
|
||||
),
|
||||
),
|
||||
("client_ip", models.GenericIPAddressField(null=True)),
|
||||
("request_ip", models.GenericIPAddressField()),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
(
|
||||
"user",
|
||||
|
@ -66,8 +65,8 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Audit Event",
|
||||
"verbose_name_plural": "Audit Events",
|
||||
"verbose_name": "Audit Entry",
|
||||
"verbose_name_plural": "Audit Entries",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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",),
|
||||
]
|
|
@ -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"),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -5,7 +5,7 @@ from django.test import TestCase
|
|||
from guardian.shortcuts import get_anonymous_user
|
||||
|
||||
from passbook.audit.models import Event, EventAction
|
||||
from passbook.policies.models import Policy
|
||||
from passbook.core.models import Policy
|
||||
|
||||
|
||||
class TestAuditEvent(TestCase):
|
||||
|
|
|
@ -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"
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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",),
|
||||
),
|
||||
]
|
|
@ -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")
|
|
@ -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",
|
||||
]
|
|
@ -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
|
|
@ -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/"
|
|
@ -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",),
|
||||
),
|
||||
]
|
|
@ -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})
|
||||
)
|
|
@ -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
|
|
@ -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 = ""
|
|
@ -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"
|
|
@ -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",),
|
||||
),
|
||||
]
|
|
@ -1,6 +0,0 @@
|
|||
"""saml provider settings"""
|
||||
|
||||
PASSBOOK_PROVIDERS_SAML_PROCESSORS = [
|
||||
"passbook.channels.out_saml.processors.generic",
|
||||
"passbook.channels.out_saml.processors.salesforce",
|
||||
]
|
|
@ -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/"
|
|
@ -16,7 +16,7 @@ class ApplicationSerializer(ModelSerializer):
|
|||
"name",
|
||||
"slug",
|
||||
"skip_authorization",
|
||||
"outlet",
|
||||
"provider",
|
||||
"meta_launch_url",
|
||||
"meta_icon_url",
|
||||
"meta_description",
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
|
@ -2,8 +2,8 @@
|
|||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
from passbook.core.models import Policy
|
||||
from passbook.policies.forms import GENERAL_FIELDS
|
||||
from passbook.policies.models import Policy
|
||||
|
||||
|
||||
class PolicySerializer(ModelSerializer):
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
|
@ -3,14 +3,14 @@ from django import forms
|
|||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
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):
|
||||
"""Application Form"""
|
||||
|
||||
outlet = forms.ModelChoiceField(
|
||||
queryset=Outlet.objects.all().order_by("pk").select_subclasses(),
|
||||
provider = forms.ModelChoiceField(
|
||||
queryset=Provider.objects.all().order_by("pk").select_subclasses(),
|
||||
required=False,
|
||||
)
|
||||
|
||||
|
@ -21,7 +21,7 @@ class ApplicationForm(forms.ModelForm):
|
|||
"name",
|
||||
"slug",
|
||||
"skip_authorization",
|
||||
"outlet",
|
||||
"provider",
|
||||
"meta_launch_url",
|
||||
"meta_icon_url",
|
||||
"meta_description",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -7,7 +7,6 @@ import django.contrib.auth.validators
|
|||
import django.contrib.postgres.fields.jsonb
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import guardian.mixins
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
@ -20,7 +19,6 @@ class Migration(migrations.Migration):
|
|||
|
||||
dependencies = [
|
||||
("auth", "0011_update_proxy_permissions"),
|
||||
("passbook_policies", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -107,41 +105,63 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
),
|
||||
("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)),
|
||||
(
|
||||
"attributes",
|
||||
django.contrib.postgres.fields.jsonb.JSONField(
|
||||
blank=True, default=dict
|
||||
),
|
||||
),
|
||||
],
|
||||
options={"permissions": (("reset_user_password", "Reset Password"),),},
|
||||
bases=(guardian.mixins.GuardianUserMixin, models.Model),
|
||||
options={
|
||||
"verbose_name": "user",
|
||||
"verbose_name_plural": "users",
|
||||
"abstract": False,
|
||||
},
|
||||
managers=[("objects", django.contrib.auth.models.UserManager()),],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Inlet",
|
||||
name="Policy",
|
||||
fields=[
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("last_updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"policybindingmodel_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_policies.PolicyBindingModel",
|
||||
),
|
||||
),
|
||||
("name", models.TextField(help_text="Inlet's display Name.")),
|
||||
("name", models.TextField(blank=True, null=True)),
|
||||
(
|
||||
"slug",
|
||||
models.SlugField(help_text="Internal source name, used in URLs."),
|
||||
"action",
|
||||
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(
|
||||
name="PropertyMapping",
|
||||
|
@ -156,7 +176,6 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
("expression", models.TextField()),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Property Mapping",
|
||||
|
@ -164,38 +183,74 @@ class Migration(migrations.Migration):
|
|||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="UserInletConnection",
|
||||
name="DebugPolicy",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
"policy_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("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,
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
("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(
|
||||
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=[
|
||||
(
|
||||
"id",
|
||||
|
@ -215,7 +270,7 @@ class Migration(migrations.Migration):
|
|||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Token",
|
||||
name="Nonce",
|
||||
fields=[
|
||||
(
|
||||
"uuid",
|
||||
|
@ -229,11 +284,10 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"expires",
|
||||
models.DateTimeField(
|
||||
default=passbook.core.models.default_token_duration
|
||||
default=passbook.core.models.default_nonce_duration
|
||||
),
|
||||
),
|
||||
("expiring", models.BooleanField(default=True)),
|
||||
("description", models.TextField(blank=True, default="")),
|
||||
(
|
||||
"user",
|
||||
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(
|
||||
model_name="inlet",
|
||||
name="property_mappings",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, default=None, to="passbook_core.PropertyMapping"
|
||||
migrations.CreateModel(
|
||||
name="Invitation",
|
||||
fields=[
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("expires", models.DateTimeField(blank=True, default=None, null=True)),
|
||||
("fixed_username", models.TextField(blank=True, default=None)),
|
||||
("fixed_email", models.TextField(blank=True, default=None)),
|
||||
("needs_confirmation", models.BooleanField(default=True)),
|
||||
(
|
||||
"created_by",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Invitation",
|
||||
"verbose_name_plural": "Invitations",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Group",
|
||||
fields=[
|
||||
|
@ -265,7 +341,7 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
("name", models.CharField(max_length=80, verbose_name="name")),
|
||||
(
|
||||
"attributes",
|
||||
"tags",
|
||||
django.contrib.postgres.fields.jsonb.JSONField(
|
||||
blank=True, default=dict
|
||||
),
|
||||
|
@ -283,57 +359,11 @@ class Migration(migrations.Migration):
|
|||
],
|
||||
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(
|
||||
model_name="user",
|
||||
name="groups",
|
||||
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(
|
||||
model_name="user",
|
||||
name="user_permissions",
|
||||
|
@ -347,33 +377,74 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="PropertyMappingBinding",
|
||||
name="UserSourceConnection",
|
||||
fields=[
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
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(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="passbook_core.Outlet",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"property_mapping",
|
||||
"source",
|
||||
models.ForeignKey(
|
||||
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"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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"),)},
|
||||
),
|
||||
]
|
|
@ -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=""),
|
||||
),
|
||||
]
|
|
@ -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
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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 = []
|
|
@ -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",),
|
||||
]
|
|
@ -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 = []
|
|
@ -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,
|
||||
),
|
||||
]
|
|
@ -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",
|
||||
),
|
||||
]
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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."),
|
||||
),
|
||||
]
|
|
@ -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=""),
|
||||
),
|
||||
]
|
|
@ -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)]
|
|
@ -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",),
|
||||
]
|
|
@ -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",),
|
||||
]
|
|
@ -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",),
|
||||
]
|
|
@ -10,6 +10,7 @@ from django.db import models
|
|||
from django.http import HttpRequest
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_prometheus.models import ExportModelOperationsMixin
|
||||
from guardian.mixins import GuardianUserMixin
|
||||
from jinja2 import Undefined
|
||||
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.types import UILoginButton, UIUserSettings
|
||||
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()
|
||||
NATIVE_ENVIRONMENT = NativeEnvironment()
|
||||
|
||||
|
||||
def default_token_duration():
|
||||
"""Default duration a Token is valid"""
|
||||
def default_nonce_duration():
|
||||
"""Default duration a Nonce is valid"""
|
||||
return now() + timedelta(minutes=30)
|
||||
|
||||
|
||||
class Group(UUIDModel):
|
||||
class Group(ExportModelOperationsMixin("group"), UUIDModel):
|
||||
"""Custom Group model which supports a basic hierarchy"""
|
||||
|
||||
name = models.CharField(_("name"), max_length=80)
|
||||
|
@ -53,13 +55,13 @@ class Group(UUIDModel):
|
|||
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"""
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, editable=False)
|
||||
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")
|
||||
password_change_date = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
@ -76,7 +78,29 @@ class User(GuardianUserMixin, AbstractUser):
|
|||
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
|
||||
needs an Application record. Other authentication types can subclass this Model to
|
||||
add custom fields and other properties"""
|
||||
|
@ -84,8 +108,8 @@ class Application(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)
|
||||
outlet = models.OneToOneField(
|
||||
"Outlet", null=True, blank=True, default=None, on_delete=models.SET_DEFAULT
|
||||
provider = models.OneToOneField(
|
||||
"Provider", null=True, blank=True, default=None, on_delete=models.SET_DEFAULT
|
||||
)
|
||||
|
||||
meta_launch_url = models.URLField(default="", blank=True)
|
||||
|
@ -95,20 +119,20 @@ class Application(PolicyBindingModel):
|
|||
|
||||
objects = InheritanceManager()
|
||||
|
||||
def get_outlet(self) -> Optional["Outlet"]:
|
||||
"""Get casted outlet instance"""
|
||||
if not self.outlet:
|
||||
def get_provider(self) -> Optional[Provider]:
|
||||
"""Get casted provider instance"""
|
||||
if not self.provider:
|
||||
return None
|
||||
return Outlet.objects.get_subclass(pk=self.outlet.pk)
|
||||
return Provider.objects.get_subclass(pk=self.provider.pk)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Inlet(PolicyBindingModel):
|
||||
class Source(ExportModelOperationsMixin("source"), PolicyModel):
|
||||
"""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."))
|
||||
|
||||
enabled = models.BooleanField(default=True)
|
||||
|
@ -141,69 +165,56 @@ class Inlet(PolicyBindingModel):
|
|||
return self.name
|
||||
|
||||
|
||||
class UserInletConnection(CreatedUpdatedModel):
|
||||
"""Connection between User and Inlet."""
|
||||
class UserSourceConnection(CreatedUpdatedModel):
|
||||
"""Connection between User and Source."""
|
||||
|
||||
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:
|
||||
|
||||
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"""
|
||||
|
||||
expires = models.DateTimeField(default=default_token_duration)
|
||||
user = models.ForeignKey("User", on_delete=models.CASCADE, related_name="+")
|
||||
expires = models.DateTimeField(default=default_nonce_duration)
|
||||
user = models.ForeignKey("User", on_delete=models.CASCADE)
|
||||
expiring = models.BooleanField(default=True)
|
||||
description = models.TextField(default="", blank=True)
|
||||
|
||||
@property
|
||||
def is_expired(self) -> bool:
|
||||
"""Check if token is expired yet."""
|
||||
"""Check if nonce is expired yet."""
|
||||
return now() > self.expires
|
||||
|
||||
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:
|
||||
|
||||
verbose_name = _("Token")
|
||||
verbose_name_plural = _("Tokens")
|
||||
|
||||
|
||||
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"),)
|
||||
verbose_name = _("Nonce")
|
||||
verbose_name_plural = _("Nonces")
|
||||
|
||||
|
||||
class PropertyMapping(UUIDModel):
|
||||
|
|
|
@ -17,7 +17,7 @@ password_changed = Signal(providing_args=["user", "password"])
|
|||
# pylint: disable=unused-argument
|
||||
def invalidate_policy_cache(sender, instance, **_):
|
||||
"""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
|
||||
|
||||
if isinstance(instance, Policy):
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
from django.utils.timezone import now
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.core.models import Token
|
||||
from passbook.core.models import Nonce
|
||||
from passbook.root.celery import CELERY_APP
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
@CELERY_APP.task()
|
||||
def clean_tokens():
|
||||
"""Remove expired tokens"""
|
||||
amount, _ = Token.objects.filter(expires__lt=now(), expiring=True).delete()
|
||||
LOGGER.debug("Deleted expired tokens", amount=amount)
|
||||
def clean_nonces():
|
||||
"""Remove expired nonces"""
|
||||
amount, _ = Nonce.objects.filter(expires__lt=now(), expiring=True).delete()
|
||||
LOGGER.debug("Deleted expired nonces", amount=amount)
|
||||
|
|
|
@ -34,17 +34,17 @@
|
|||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% user_inlets as user_inlets_loc %}
|
||||
{% if user_inlets_loc %}
|
||||
{% user_sources as user_sources_loc %}
|
||||
{% if user_sources_loc %}
|
||||
<section class="pf-c-nav__section">
|
||||
<h2 class="pf-c-nav__section-title">{% trans 'Sources' %}</h2>
|
||||
<ul class="pf-c-nav__list">
|
||||
{% for inlet in user_inlets_loc %}
|
||||
{% for source in user_sources_loc %}
|
||||
<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 %}">
|
||||
<i class="{{ inlet.icon }}"></i>
|
||||
{{ inlet.name }}
|
||||
<i class="{{ source.icon }}"></i>
|
||||
{{ source.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -4,7 +4,7 @@ from typing import Iterable, List
|
|||
from django import template
|
||||
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.flows.models import Stage
|
||||
from passbook.policies.engine import PolicyEngine
|
||||
|
@ -27,14 +27,14 @@ def user_stages(context: RequestContext) -> List[UIUserSettings]:
|
|||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def user_inlets(context: RequestContext) -> List[UIUserSettings]:
|
||||
"""Return a list of all inlets which are enabled for the user"""
|
||||
def user_sources(context: RequestContext) -> List[UIUserSettings]:
|
||||
"""Return a list of all sources which are enabled for the user"""
|
||||
user = context.get("request").user
|
||||
_all_inlets: Iterable[(Inlet)] = (
|
||||
(Inlet).objects.filter(enabled=True).select_subclasses()
|
||||
_all_sources: Iterable[Source] = (
|
||||
Source.objects.filter(enabled=True).select_subclasses()
|
||||
)
|
||||
matching_inlets: List[UIUserSettings] = []
|
||||
for source in _all_inlets:
|
||||
matching_sources: List[UIUserSettings] = []
|
||||
for source in _all_sources:
|
||||
user_settings = source.ui_user_settings
|
||||
if not user_settings:
|
||||
continue
|
||||
|
@ -43,5 +43,5 @@ def user_inlets(context: RequestContext) -> List[UIUserSettings]:
|
|||
)
|
||||
policy_engine.build()
|
||||
if policy_engine.passing:
|
||||
matching_inlets.append(user_settings)
|
||||
return matching_inlets
|
||||
matching_sources.append(user_settings)
|
||||
return matching_sources
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.http import HttpRequest
|
|||
from django.utils.translation import gettext as _
|
||||
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
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
@ -14,19 +14,22 @@ LOGGER = get_logger()
|
|||
|
||||
class AccessMixin:
|
||||
"""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: HttpRequest = None
|
||||
|
||||
def outlet_to_application(self, outlet: Outlet) -> Application:
|
||||
"""Lookup application assigned to outlet, throw error if no application assigned"""
|
||||
def provider_to_application(self, provider: Provider) -> Application:
|
||||
"""Lookup application assigned to provider, throw error if no application assigned"""
|
||||
try:
|
||||
return outlet.application
|
||||
return provider.application
|
||||
except Application.DoesNotExist as exc:
|
||||
messages.error(
|
||||
self.request,
|
||||
_('Outlet "%(name)s" has no application assigned' % {"name": outlet}),
|
||||
_(
|
||||
'Provider "%(name)s" has no application assigned'
|
||||
% {"name": provider}
|
||||
),
|
||||
)
|
||||
raise exc
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
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):
|
||||
|
||||
initial = True
|
||||
|
@ -27,22 +41,27 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
(
|
||||
"certificate_data",
|
||||
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.",
|
||||
),
|
||||
),
|
||||
("certificate_data", models.TextField()),
|
||||
("key_data", models.TextField(blank=True, default="")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Certificate-Key Pair",
|
||||
"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.",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -11,7 +11,7 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_policies", "__first__"),
|
||||
("passbook_policies", "0003_auto_20200508_1642"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -33,12 +33,10 @@ class Migration(migrations.Migration):
|
|||
"designation",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("authentication", "Authentication"),
|
||||
("invalidation", "Invalidation"),
|
||||
("enrollment", "Enrollment"),
|
||||
("unenrollment", "Unrenollment"),
|
||||
("recovery", "Recovery"),
|
||||
("password_change", "Password Change"),
|
||||
("AUTHENTICATION", "authentication"),
|
||||
("ENROLLMENT", "enrollment"),
|
||||
("RECOVERY", "recovery"),
|
||||
("PASSWORD_CHANGE", "password_change"),
|
||||
],
|
||||
max_length=100,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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
|
||||
from django.db import migrations, models
|
||||
|
@ -9,7 +9,8 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_policies", "0001_initial"),
|
||||
("passbook_policies", "0003_auto_20200508_1642"),
|
||||
("passbook_core", "0013_delete_debugpolicy"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -24,7 +25,7 @@ class Migration(migrations.Migration):
|
|||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_policies.Policy",
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
("result", models.BooleanField(default=False)),
|
||||
|
@ -35,6 +36,6 @@ class Migration(migrations.Migration):
|
|||
"verbose_name": "Dummy Policy",
|
||||
"verbose_name_plural": "Dummy Policies",
|
||||
},
|
||||
bases=("passbook_policies.policy",),
|
||||
bases=("passbook_core.policy",),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.db import models
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.policies.models import Policy
|
||||
from passbook.core.models import Policy
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
|
|
@ -7,8 +7,7 @@ from django.core.cache import cache
|
|||
from django.http import HttpRequest
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.core.models import User
|
||||
from passbook.policies.models import Policy
|
||||
from passbook.core.models import Policy, User
|
||||
from passbook.policies.process import PolicyProcess, cache_key
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
|
|
|
@ -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
|
||||
from django.db import migrations, models
|
||||
|
@ -9,7 +9,7 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_policies", "0001_initial"),
|
||||
("passbook_core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
|
|||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_policies.Policy",
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
("deny_only", models.BooleanField(default=False)),
|
||||
|
@ -34,6 +34,6 @@ class Migration(migrations.Migration):
|
|||
"verbose_name": "Password Expiry Policy",
|
||||
"verbose_name_plural": "Password Expiry Policies",
|
||||
},
|
||||
bases=("passbook_policies.policy",),
|
||||
bases=("passbook_core.policy",),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.utils.timezone import now
|
|||
from django.utils.translation import gettext as _
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.policies.models import Policy
|
||||
from passbook.core.models import Policy
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
|
|
@ -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
|
||||
from django.db import migrations, models
|
||||
|
@ -9,7 +9,7 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_policies", "0001_initial"),
|
||||
("passbook_core", "0007_auto_20200217_1934"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
|
|||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_policies.Policy",
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
("expression", models.TextField()),
|
||||
|
@ -33,6 +33,6 @@ class Migration(migrations.Migration):
|
|||
"verbose_name": "Expression Policy",
|
||||
"verbose_name_plural": "Expression Policies",
|
||||
},
|
||||
bases=("passbook_policies.policy",),
|
||||
bases=("passbook_core.policy",),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from passbook.core.models import Policy
|
||||
from passbook.policies.expression.evaluator import Evaluator
|
||||
from passbook.policies.models import Policy
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
from django.db import migrations, models
|
||||
|
@ -9,7 +9,7 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_policies", "0001_initial"),
|
||||
("passbook_core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
|
|||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_policies.Policy",
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
("allowed_count", models.IntegerField(default=0)),
|
||||
|
@ -33,6 +33,6 @@ class Migration(migrations.Migration):
|
|||
"verbose_name": "Have I Been Pwned Policy",
|
||||
"verbose_name_plural": "Have I Been Pwned Policies",
|
||||
},
|
||||
bases=("passbook_policies.policy",),
|
||||
bases=("passbook_core.policy",),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,8 +6,7 @@ from django.utils.translation import gettext as _
|
|||
from requests import get
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.policies.models import Policy
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
from passbook.core.models import Policy, PolicyResult, User
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
@ -20,14 +19,14 @@ class HaveIBeenPwendPolicy(Policy):
|
|||
|
||||
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
|
||||
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."""
|
||||
# Only check if password is being set
|
||||
if not hasattr(request.user, "__password__"):
|
||||
if not hasattr(user, "__password__"):
|
||||
return PolicyResult(True)
|
||||
password = getattr(request.user, "__password__")
|
||||
password = getattr(user, "__password__")
|
||||
pw_hash = sha1(password.encode("utf-8")).hexdigest() # nosec
|
||||
url = "https://api.pwnedpasswords.com/range/%s" % pw_hash[:5]
|
||||
result = get(url).text
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -10,30 +10,11 @@ class Migration(migrations.Migration):
|
|||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
dependencies = [
|
||||
("passbook_core", "0011_auto_20200222_1822"),
|
||||
]
|
||||
|
||||
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(
|
||||
name="PolicyBinding",
|
||||
fields=[
|
||||
|
@ -53,7 +34,7 @@ class Migration(migrations.Migration):
|
|||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="passbook_policies.Policy",
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -77,17 +58,12 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"policies",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="_policybindingmodel_policies_+",
|
||||
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(
|
||||
model_name="policybinding",
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
),
|
||||
]
|
|
@ -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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,30 +1,9 @@
|
|||
"""Policy base models"""
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from model_utils.managers import InheritanceManager
|
||||
|
||||
from passbook.lib.models import CreatedUpdatedModel, UUIDModel
|
||||
from passbook.policies.exceptions import PolicyException
|
||||
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()
|
||||
from passbook.core.models import Policy
|
||||
from passbook.lib.models import UUIDModel
|
||||
|
||||
|
||||
class PolicyBindingModel(models.Model):
|
||||
|
|
|
@ -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
|
||||
from django.db import migrations, models
|
||||
|
@ -9,7 +9,7 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_policies", "0001_initial"),
|
||||
("passbook_core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
|
|||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_policies.Policy",
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
("amount_uppercase", models.IntegerField(default=0)),
|
||||
|
@ -41,6 +41,6 @@ class Migration(migrations.Migration):
|
|||
"verbose_name": "Password Policy",
|
||||
"verbose_name_plural": "Password Policies",
|
||||
},
|
||||
bases=("passbook_policies.policy",),
|
||||
bases=("passbook_core.policy",),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.db import models
|
|||
from django.utils.translation import gettext as _
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.policies.models import Policy
|
||||
from passbook.core.models import Policy
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
|
|
@ -6,9 +6,8 @@ from typing import Optional
|
|||
from django.core.cache import cache
|
||||
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.models import Policy
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
|
|
@ -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
|
||||
from django.conf import settings
|
||||
|
@ -10,7 +10,7 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_policies", "0001_initial"),
|
||||
("passbook_core", "0001_initial"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
|
@ -43,7 +43,7 @@ class Migration(migrations.Migration):
|
|||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_policies.Policy",
|
||||
to="passbook_core.Policy",
|
||||
),
|
||||
),
|
||||
("check_ip", models.BooleanField(default=True)),
|
||||
|
@ -54,7 +54,7 @@ class Migration(migrations.Migration):
|
|||
"verbose_name": "Reputation Policy",
|
||||
"verbose_name_plural": "Reputation Policies",
|
||||
},
|
||||
bases=("passbook_policies.policy",),
|
||||
bases=("passbook_core.policy",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="UserReputation",
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
from django.db import models
|
||||
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.policies.models import Policy
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
from django.core.cache import cache
|
||||
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.engine import PolicyEngine
|
||||
from passbook.policies.models import Policy
|
||||
|
||||
|
||||
class PolicyTestEngine(TestCase):
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
"""ApplicationGatewayOutlet API Views"""
|
||||
"""ApplicationGatewayProvider API Views"""
|
||||
from oauth2_provider.generators import generate_client_id, generate_client_secret
|
||||
from oidc_provider.models import Client
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from passbook.channels.out_app_gw.models import ApplicationGatewayOutlet
|
||||
from passbook.channels.out_oidc.api import OpenIDOutletSerializer
|
||||
from passbook.providers.app_gw.models import ApplicationGatewayProvider
|
||||
from passbook.providers.oidc.api import OpenIDProviderSerializer
|
||||
|
||||
|
||||
class ApplicationGatewayOutletSerializer(ModelSerializer):
|
||||
"""ApplicationGatewayOutlet Serializer"""
|
||||
class ApplicationGatewayProviderSerializer(ModelSerializer):
|
||||
"""ApplicationGatewayProvider Serializer"""
|
||||
|
||||
client = OpenIDOutletSerializer()
|
||||
client = OpenIDProviderSerializer()
|
||||
|
||||
def create(self, validated_data):
|
||||
instance = super().create(validated_data)
|
||||
|
@ -33,13 +33,13 @@ class ApplicationGatewayOutletSerializer(ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
|
||||
model = ApplicationGatewayOutlet
|
||||
model = ApplicationGatewayProvider
|
||||
fields = ["pk", "name", "internal_host", "external_host", "client"]
|
||||
read_only_fields = ["client"]
|
||||
|
||||
|
||||
class ApplicationGatewayOutletViewSet(ModelViewSet):
|
||||
"""ApplicationGatewayOutlet Viewset"""
|
||||
class ApplicationGatewayProviderViewSet(ModelViewSet):
|
||||
"""ApplicationGatewayProvider Viewset"""
|
||||
|
||||
queryset = ApplicationGatewayOutlet.objects.all()
|
||||
serializer_class = ApplicationGatewayOutletSerializer
|
||||
queryset = ApplicationGatewayProvider.objects.all()
|
||||
serializer_class = ApplicationGatewayProviderSerializer
|
|
@ -5,7 +5,7 @@ from django.apps import AppConfig
|
|||
class PassbookApplicationApplicationGatewayConfig(AppConfig):
|
||||
"""passbook app_gw app"""
|
||||
|
||||
name = "passbook.channels.out_app_gw"
|
||||
label = "passbook_channels_out_app_gw"
|
||||
verbose_name = "passbook Outlets.Application Security Gateway"
|
||||
name = "passbook.providers.app_gw"
|
||||
label = "passbook_providers_app_gw"
|
||||
verbose_name = "passbook Providers.Application Security Gateway"
|
||||
mountpoint = "application/gateway/"
|
|
@ -3,11 +3,11 @@ from django import forms
|
|||
from oauth2_provider.generators import generate_client_id, generate_client_secret
|
||||
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):
|
||||
"""Security Gateway Outlet form"""
|
||||
class ApplicationGatewayProviderForm(forms.ModelForm):
|
||||
"""Security Gateway Provider form"""
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.instance.pk:
|
||||
|
@ -31,7 +31,7 @@ class ApplicationGatewayOutletForm(forms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
|
||||
model = ApplicationGatewayOutlet
|
||||
model = ApplicationGatewayProvider
|
||||
fields = ["name", "internal_host", "external_host"]
|
||||
widgets = {
|
||||
"name": forms.TextInput(),
|
|
@ -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",),
|
||||
),
|
||||
]
|
|
@ -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",),
|
||||
]
|
|
@ -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
|
||||
from django.db import migrations, models
|
||||
|
@ -9,28 +9,28 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_core", "__first__"),
|
||||
("passbook_core", "0005_merge_20191025_2022"),
|
||||
("oidc_provider", "0026_client_multiple_response_types"),
|
||||
("passbook_providers_app_gw", "0002_auto_20191111_1703"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ApplicationGatewayOutlet",
|
||||
name="ApplicationGatewayProvider",
|
||||
fields=[
|
||||
(
|
||||
"outlet_ptr",
|
||||
"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.Outlet",
|
||||
to="passbook_core.Provider",
|
||||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
("internal_host", models.TextField()),
|
||||
("external_host", models.TextField()),
|
||||
("host", models.TextField()),
|
||||
(
|
||||
"client",
|
||||
models.ForeignKey(
|
||||
|
@ -40,9 +40,9 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Application Gateway Outlet",
|
||||
"verbose_name_plural": "Application Gateway Outlets",
|
||||
"verbose_name": "Application Gateway Provider",
|
||||
"verbose_name_plural": "Application Gateway Providers",
|
||||
},
|
||||
bases=("passbook_core.outlet",),
|
||||
bases=("passbook_core.provider",),
|
||||
),
|
||||
]
|
|
@ -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,
|
||||
),
|
||||
]
|
|
@ -9,12 +9,12 @@ from django.utils.translation import gettext as _
|
|||
from oidc_provider.models import Client
|
||||
|
||||
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
|
||||
|
||||
|
||||
class ApplicationGatewayOutlet(Outlet):
|
||||
"""This outlet uses oauth2_proxy with the OIDC Outlet."""
|
||||
class ApplicationGatewayProvider(Provider):
|
||||
"""This provider uses oauth2_proxy with the OIDC Provider."""
|
||||
|
||||
name = models.TextField()
|
||||
internal_host = models.TextField()
|
||||
|
@ -22,7 +22,7 @@ class ApplicationGatewayOutlet(Outlet):
|
|||
|
||||
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]:
|
||||
"""return template and context modal with URLs for authorize, token, openid-config, etc"""
|
||||
|
@ -32,7 +32,7 @@ class ApplicationGatewayOutlet(Outlet):
|
|||
)
|
||||
return render_to_string(
|
||||
"app_gw/setup_modal.html",
|
||||
{"outlet": self, "cookie_secret": cookie_secret, "version": __version__},
|
||||
{"provider": self, "cookie_secret": cookie_secret, "version": __version__},
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -40,5 +40,5 @@ class ApplicationGatewayOutlet(Outlet):
|
|||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _("Application Gateway Outlet")
|
||||
verbose_name_plural = _("Application Gateway Outlets")
|
||||
verbose_name = _("Application Gateway Provider")
|
||||
verbose_name_plural = _("Application Gateway Providers")
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue