feat: Add trust bridge and user activation

This commit is contained in:
aldrin 2021-08-02 14:25:58 +02:00
parent 74afc805dc
commit 0f1cd98a80
8 changed files with 78 additions and 52 deletions

View file

@ -43,12 +43,14 @@ export class User {
email: string | undefined; email: string | undefined;
password: string | undefined; password: string | undefined;
username: string | null = null; username: string | null = null;
confidantEmail: string | null = null;
isAuthenticated = false; isAuthenticated = false;
isTrusted = false;
private token = ""; private token = "";
static async confirm(uid: string, token: string): Promise<void> { async activate(): Promise<void> {
await api_request("POST", "users/activation", 204, { uid, token }); await api_request("POST", "users/activate", 204, {
password: this.password,
});
} }
async login(): Promise<void> { async login(): Promise<void> {
@ -84,7 +86,6 @@ export class User {
200, 200,
{ {
username: this.username, username: this.username,
confidant_email: this.confidantEmail,
}, },
this.token this.token
); );

View file

@ -1,20 +1,8 @@
<template> <template>
<div> <div>
<b-notification <b-notification type="is-info" aria-close-label="Close notification">
v-if="!user.username" Dein Konto ist noch nicht aktiv.
type="is-warning" <a @click="activate()">Jetzt aktivieren</a>
aria-close-label="Close notification"
>
Um dein Konto zu aktivieren, musst du einen
<strong>Benutzernamen</strong> festlegen.
</b-notification>
<b-notification
v-if="!user.confidant_email"
type="is-warning"
aria-close-label="Close notification"
>
Um dein Konto zu aktivieren, musst du eine
<strong>Vertrauensperson</strong> angeben.
</b-notification> </b-notification>
<table class="table is-fullwidth"> <table class="table is-fullwidth">
<tbody> <tbody>
@ -34,10 +22,6 @@
<inline-editor v-model="user.confidantEmail" @input="user.save()" /> <inline-editor v-model="user.confidantEmail" @input="user.save()" />
</td> </td>
</tr> </tr>
<tr>
<td>E-Mail-Adresse</td>
<td>{{ user.email }} (bestätigt)</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -53,5 +37,9 @@ import InlineEditor from "@/components/InlineEditor.vue";
}) })
export default class UserTable extends Vue { export default class UserTable extends Vue {
@Prop() private user!: User; @Prop() private user!: User;
async activate(): Promise<void> {
await this.user.activate();
}
} }
</script> </script>

View file

@ -1,5 +1,6 @@
from django.contrib import admin from django.contrib import admin
from userausfall.models import User from userausfall.models import User, TrustBridge
admin.site.register(TrustBridge)
admin.site.register(User) admin.site.register(User)

View file

@ -0,0 +1,29 @@
# Generated by Django 2.2.20 on 2021-08-02 11:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('userausfall', '0009_auto_20210802_0745'),
]
operations = [
migrations.RemoveField(
model_name='user',
name='confidant',
),
migrations.RemoveField(
model_name='user',
name='confidant_unconfirmed',
),
migrations.RemoveField(
model_name='user',
name='email',
),
migrations.RemoveField(
model_name='user',
name='email_unconfirmed',
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 2.2.20 on 2021-08-02 11:41
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('userausfall', '0010_auto_20210802_1131'),
]
operations = [
migrations.CreateModel(
name='TrustBridge',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_trusted', models.BooleanField(default=False)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='trust_bridge', to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -66,28 +66,16 @@ class User(AbstractBaseUser, PermissionsMixin):
unique=True, unique=True,
blank=True, blank=True,
) )
email = models.EmailField(_("email address"), blank=True)
email_unconfirmed = models.EmailField(_("email address"), blank=True)
is_staff = models.BooleanField( is_staff = models.BooleanField(
_("staff status"), _("staff status"),
default=False, default=False,
help_text=_("Designates whether the user can log into this admin site."), help_text=_("Designates whether the user can log into this admin site."),
) )
date_joined = models.DateTimeField(_("date joined"), default=timezone.now) date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
confidant = models.ForeignKey(
"User", on_delete=models.SET_NULL, null=True, blank=True, related_name="confidants"
)
confidant_unconfirmed = models.ForeignKey(
"User",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="unconfirmed_confidants",
)
objects = UserManager() objects = UserManager()
EMAIL_FIELD = "email" # EMAIL_FIELD = "email"
USERNAME_FIELD = "username" USERNAME_FIELD = "username"
REQUIRED_FIELDS = [] REQUIRED_FIELDS = []
@ -95,13 +83,6 @@ class User(AbstractBaseUser, PermissionsMixin):
verbose_name = _("user") verbose_name = _("user")
verbose_name_plural = _("users") verbose_name_plural = _("users")
@property
def confidant_email(self):
if self.confidant is not None:
return self.confidant.email
else:
return None
def clean(self): def clean(self):
super().clean() super().clean()
self.email = self.__class__.objects.normalize_email(self.email) self.email = self.__class__.objects.normalize_email(self.email)
@ -114,10 +95,13 @@ class User(AbstractBaseUser, PermissionsMixin):
"""Create the LDAP account which corresponds to this user.""" """Create the LDAP account which corresponds to this user."""
if not self.username: if not self.username:
raise MissingUserAttribute("User is missing a username.") raise MissingUserAttribute("User is missing a username.")
if not self.confidant:
raise MissingUserAttribute("User is missing a confirmed confidant.")
if not self.check_password(raw_password): if not self.check_password(raw_password):
raise PasswordMismatch( raise PasswordMismatch(
"The given password does not match the user's password." "The given password does not match the user's password."
) )
return ldap.create_account(self.username, raw_password) return ldap.create_account(self.username, raw_password)
class TrustBridge(models.Model):
user = models.OneToOneField("User", on_delete=models.CASCADE, related_name="trust_bridge")
is_trusted = models.BooleanField(default=False)

View file

@ -14,13 +14,13 @@ class ConfidantConfirmationView(ConfirmationView):
class UserViewSet(viewsets.ModelViewSet): class UserViewSet(viewsets.ModelViewSet):
permission_classes = [UserPermission] # permission_classes = [UserPermission]
queryset = User.objects.all() queryset = User.objects.all()
@action(detail=True, methods=["post"]) @action(detail=False, methods=["post"])
def activate(self, request, pk=None): def activate(self, request, pk=None):
"""Create the corresponding LDAP account.""" """Create the corresponding LDAP account."""
user: User = self.get_object() user: User = request.user # self.get_object()
serializer = UserActivationSerializer(data=request.data) serializer = UserActivationSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
try: try:

View file

@ -2,10 +2,10 @@ from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from userausfall.models import User from userausfall.models import User
from userausfall.confirmations import ConfidantConfirmation
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def user_saved(sender, instance: User, **kwargs): def user_saved(sender, instance: User, **kwargs):
if instance.confidant_unconfirmed is not None: # if instance.confidant_unconfirmed is not None:
ConfidantConfirmation(instance.confidant_unconfirmed, instance).send_request() # ConfidantConfirmation(instance.confidant_unconfirmed, instance).send_request()
pass