core: properly handle invites; audit: log invite creation and usage
This commit is contained in:
parent
274c9daded
commit
64c8458c90
|
@ -24,6 +24,7 @@ class AuditEntry(UUIDModel):
|
||||||
ACTION_SUSPICIOUS_REQUEST = 'suspicious_request'
|
ACTION_SUSPICIOUS_REQUEST = 'suspicious_request'
|
||||||
ACTION_SIGN_UP = 'sign_up'
|
ACTION_SIGN_UP = 'sign_up'
|
||||||
ACTION_PASSWORD_RESET = 'password_reset'
|
ACTION_PASSWORD_RESET = 'password_reset'
|
||||||
|
ACTION_INVITE_CREATED = 'invite_created'
|
||||||
ACTION_INVITE_USED = 'invite_used'
|
ACTION_INVITE_USED = 'invite_used'
|
||||||
ACTIONS = (
|
ACTIONS = (
|
||||||
(ACTION_LOGIN, ACTION_LOGIN),
|
(ACTION_LOGIN, ACTION_LOGIN),
|
||||||
|
@ -33,6 +34,7 @@ class AuditEntry(UUIDModel):
|
||||||
(ACTION_SUSPICIOUS_REQUEST, ACTION_SUSPICIOUS_REQUEST),
|
(ACTION_SUSPICIOUS_REQUEST, ACTION_SUSPICIOUS_REQUEST),
|
||||||
(ACTION_SIGN_UP, ACTION_SIGN_UP),
|
(ACTION_SIGN_UP, ACTION_SIGN_UP),
|
||||||
(ACTION_PASSWORD_RESET, ACTION_PASSWORD_RESET),
|
(ACTION_PASSWORD_RESET, ACTION_PASSWORD_RESET),
|
||||||
|
(ACTION_INVITE_CREATED, ACTION_INVITE_CREATED),
|
||||||
(ACTION_INVITE_USED, ACTION_INVITE_USED),
|
(ACTION_INVITE_USED, ACTION_INVITE_USED),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.contrib.auth.signals import (user_logged_in, user_logged_out,
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from passbook.audit.models import AuditEntry
|
from passbook.audit.models import AuditEntry
|
||||||
|
from passbook.core.signals import invite_created, invite_used, user_signed_up
|
||||||
|
|
||||||
|
|
||||||
@receiver(user_logged_in)
|
@receiver(user_logged_in)
|
||||||
|
@ -16,6 +17,21 @@ def on_user_logged_out(sender, request, user, **kwargs):
|
||||||
"""Log successfully logout"""
|
"""Log successfully logout"""
|
||||||
AuditEntry.create(AuditEntry.ACTION_LOGOUT, request)
|
AuditEntry.create(AuditEntry.ACTION_LOGOUT, request)
|
||||||
|
|
||||||
|
@receiver(user_signed_up)
|
||||||
|
def on_user_signed_up(sender, request, user, **kwargs):
|
||||||
|
"""Log successfully signed up"""
|
||||||
|
AuditEntry.create(AuditEntry.ACTION_SIGN_UP, request)
|
||||||
|
|
||||||
|
@receiver(invite_created)
|
||||||
|
def on_invite_created(sender, request, invite, **kwargs):
|
||||||
|
"""Log Invite creation"""
|
||||||
|
AuditEntry.create(AuditEntry.ACTION_INVITE_CREATED, request, invite_uuid=invite.uuid)
|
||||||
|
|
||||||
|
@receiver(invite_used)
|
||||||
|
def on_invite_used(sender, request, invite, **kwargs):
|
||||||
|
"""Log Invite usage"""
|
||||||
|
AuditEntry.create(AuditEntry.ACTION_INVITE_USED, request, invite_uuid=invite.uuid)
|
||||||
|
|
||||||
@receiver(user_login_failed)
|
@receiver(user_login_failed)
|
||||||
def on_user_login_failed(sender, request, user, **kwargs):
|
def on_user_login_failed(sender, request, user, **kwargs):
|
||||||
"""Log failed login attempt"""
|
"""Log failed login attempt"""
|
||||||
|
|
12
passbook/core/signals.py
Normal file
12
passbook/core/signals.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
"""passbook core signals"""
|
||||||
|
|
||||||
|
from django.core.signals import Signal
|
||||||
|
|
||||||
|
# from django.db.models.signals import post_save, pre_delete
|
||||||
|
# from django.dispatch import receiver
|
||||||
|
# from passbook.core.models import Invite, User
|
||||||
|
|
||||||
|
user_signed_up = Signal(providing_args=['request', 'user'])
|
||||||
|
# TODO: Send this signal in admin interface
|
||||||
|
invite_created = Signal(providing_args=['request', 'invite'])
|
||||||
|
invite_used = Signal(providing_args=['request', 'invite', 'user'])
|
|
@ -13,6 +13,7 @@ from django.views.generic import FormView
|
||||||
|
|
||||||
from passbook.core.forms.authentication import LoginForm, SignUpForm
|
from passbook.core.forms.authentication import LoginForm, SignUpForm
|
||||||
from passbook.core.models import Invite, User
|
from passbook.core.models import Invite, User
|
||||||
|
from passbook.core.signals import invite_used, user_signed_up
|
||||||
from passbook.lib.config import CONFIG
|
from passbook.lib.config import CONFIG
|
||||||
|
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger(__name__)
|
||||||
|
@ -117,7 +118,10 @@ class SignUpView(UserPassesTestMixin, FormView):
|
||||||
template_name = 'login/form.html'
|
template_name = 'login/form.html'
|
||||||
form_class = SignUpForm
|
form_class = SignUpForm
|
||||||
success_url = '.'
|
success_url = '.'
|
||||||
|
# Invite insatnce, if invite link was used
|
||||||
_invite = None
|
_invite = None
|
||||||
|
# Instance of newly created user
|
||||||
|
_user = None
|
||||||
|
|
||||||
# Allow only not authenticated users to login
|
# Allow only not authenticated users to login
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
|
@ -150,14 +154,23 @@ class SignUpView(UserPassesTestMixin, FormView):
|
||||||
|
|
||||||
def form_valid(self, form: SignUpForm) -> HttpResponse:
|
def form_valid(self, form: SignUpForm) -> HttpResponse:
|
||||||
"""Create user"""
|
"""Create user"""
|
||||||
SignUpView.create_user(form.cleaned_data, self.request)
|
self._user = SignUpView.create_user(form.cleaned_data, self.request)
|
||||||
if self._invite:
|
self.consume_invite()
|
||||||
self._invite.delete()
|
|
||||||
messages.success(self.request, _("Successfully signed up!"))
|
messages.success(self.request, _("Successfully signed up!"))
|
||||||
LOGGER.debug("Successfully signed up %s",
|
LOGGER.debug("Successfully signed up %s",
|
||||||
form.cleaned_data.get('email'))
|
form.cleaned_data.get('email'))
|
||||||
return redirect(reverse('passbook_core:auth-login'))
|
return redirect(reverse('passbook_core:auth-login'))
|
||||||
|
|
||||||
|
def consume_invite(self):
|
||||||
|
"""Consume invite if an invite was used"""
|
||||||
|
if self._invite:
|
||||||
|
invite_used.send(
|
||||||
|
sender=self,
|
||||||
|
request=self.request,
|
||||||
|
invite=self._invite,
|
||||||
|
user=self._user)
|
||||||
|
self._invite.delete()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_user(data: Dict, request: HttpRequest = None) -> User:
|
def create_user(data: Dict, request: HttpRequest = None) -> User:
|
||||||
"""Create user from data
|
"""Create user from data
|
||||||
|
@ -183,6 +196,10 @@ class SignUpView(UserPassesTestMixin, FormView):
|
||||||
new_user.set_password(data.get('password'))
|
new_user.set_password(data.get('password'))
|
||||||
new_user.save()
|
new_user.save()
|
||||||
# Send signal for other auth sources
|
# Send signal for other auth sources
|
||||||
|
user_signed_up.send(
|
||||||
|
sender=SignUpView,
|
||||||
|
user=new_user,
|
||||||
|
request=request)
|
||||||
# try:
|
# try:
|
||||||
# TODO: Create signal for signup
|
# TODO: Create signal for signup
|
||||||
# on_user_sign_up.send(
|
# on_user_sign_up.send(
|
||||||
|
|
Reference in a new issue