feat: Allow to activate trusted accounts only

This commit is contained in:
aldrin 2021-08-03 11:41:58 +02:00
parent 0f1cd98a80
commit d656370aef
5 changed files with 40 additions and 44 deletions

View file

@ -12,7 +12,7 @@ async function api_request(
method: HTTPMethod, method: HTTPMethod,
endpoint: string, endpoint: string,
successStatus: number, successStatus: number,
data: any, data?: any,
authToken?: string authToken?: string
) { ) {
const init = { const init = {
@ -40,13 +40,15 @@ async function api_request(
} }
export class User { export class User {
email: string | undefined; pk: number | undefined;
password: string | undefined; password: string | undefined;
username: string | null = null; username: string | null = null;
isAuthenticated = false; isAuthenticated = false;
isTrusted = false; isTrusted = false;
private token = "";
/**
* Activate the corresponding LDAP account.
*/
async activate(): Promise<void> { async activate(): Promise<void> {
await api_request("POST", "users/activate", 204, { await api_request("POST", "users/activate", 204, {
password: this.password, password: this.password,
@ -74,21 +76,16 @@ export class User {
// successful logins are followed by a redirect // successful logins are followed by a redirect
if (res.redirected && res.status === 200) { if (res.redirected && res.status === 200) {
this.isAuthenticated = true; this.isAuthenticated = true;
await this.retrieve();
} else { } else {
throw new APIError("", ""); throw new APIError("", "");
} }
} }
async save(): Promise<void> { async retrieve(): Promise<void> {
await api_request( const data = await api_request("GET", "users/me", 200);
"PATCH", this.pk = data.pk;
"users/me", this.isTrusted = data.trust_bridge.is_trusted;
200,
{
username: this.username,
},
this.token
);
} }
async signup(): Promise<void> { async signup(): Promise<void> {

View file

@ -2,7 +2,7 @@
<div> <div>
<b-notification type="is-info" aria-close-label="Close notification"> <b-notification type="is-info" aria-close-label="Close notification">
Dein Konto ist noch nicht aktiv. Dein Konto ist noch nicht aktiv.
<a @click="activate()">Jetzt aktivieren</a> <a v-if="user.isTrusted" @click="activate()">Jetzt aktivieren</a>
</b-notification> </b-notification>
<table class="table is-fullwidth"> <table class="table is-fullwidth">
<tbody> <tbody>

View file

@ -27,23 +27,9 @@ export default class Home extends mixins(NotifyMixin) {
private user = new User(); private user = new User();
public async created(): Promise<void> { public async created(): Promise<void> {
if (this.$route.name === "confirm") { if (!this.user.isAuthenticated && this.$route.name !== "login") {
await this.doConfirm();
} else if (!this.user.isAuthenticated && this.$route.name !== "login") {
this.$router.push({ name: "login" }); this.$router.push({ name: "login" });
} }
} }
private async doConfirm() {
try {
await User.confirm(this.$route.params.uid, this.$route.params.token);
this.$router.push({ name: "login" });
this.showSuccess(
"Deine E-Mail-Adresse wurde bestätigt. Du kannst dich nun anmelden."
);
} catch {
this.showError();
}
}
} }
</script> </script>

View file

@ -1,25 +1,24 @@
from rest_framework import serializers from rest_framework import serializers
from userausfall.models import User from userausfall.models import User, TrustBridge
class UserActivationSerializer(serializers.Serializer): class TrustBridgeSerializer(serializers.ModelSerializer):
class Meta:
model = TrustBridge
fields = ["is_trusted"]
class ActivateUserSerializer(serializers.Serializer):
password = serializers.CharField() password = serializers.CharField()
class UserSerializer(serializers.ModelSerializer): class RetrieveUserSerializer(serializers.ModelSerializer):
confidant_email = serializers.EmailField() trust_bridge = TrustBridgeSerializer(required=False, read_only=True)
class Meta: class Meta:
model = User model = User
fields = ("pk", "email", "username", "confidant_email") fields = ["pk", "username", "trust_bridge"]
read_only_fields = ("email",)
def update(self, instance: User, validated_data):
confidant_email = validated_data.pop("confidant_email")
confidant, _ = User.objects.get_or_create(email=confidant_email)
instance.confidant_unconfirmed = confidant
return super().update(instance, validated_data)
class CreateUserSerializer(serializers.ModelSerializer): class CreateUserSerializer(serializers.ModelSerializer):

View file

@ -5,8 +5,11 @@ from rest_framework.response import Response
from djeveric import ConfirmationView from djeveric import ConfirmationView
from userausfall.models import User, MissingUserAttribute, PasswordMismatch from userausfall.models import User, MissingUserAttribute, PasswordMismatch
from userausfall.confirmations import ConfidantConfirmation from userausfall.confirmations import ConfidantConfirmation
from userausfall.rest_api.permissions import UserPermission from userausfall.rest_api.serializers import (
from userausfall.rest_api.serializers import UserSerializer, UserActivationSerializer, CreateUserSerializer ActivateUserSerializer,
CreateUserSerializer,
TrustBridgeSerializer, RetrieveUserSerializer,
)
class ConfidantConfirmationView(ConfirmationView): class ConfidantConfirmationView(ConfirmationView):
@ -17,13 +20,24 @@ class UserViewSet(viewsets.ModelViewSet):
# permission_classes = [UserPermission] # permission_classes = [UserPermission]
queryset = User.objects.all() queryset = User.objects.all()
@action(detail=False)
def me(self, request):
"""Retrieve user data for logged in user."""
user = request.user
serializer = RetrieveUserSerializer(user)
return Response(serializer.data)
@action(detail=False, 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 = request.user # self.get_object() user: User = request.user # self.get_object()
serializer = UserActivationSerializer(data=request.data) serializer = ActivateUserSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
try: try:
# We prevent untrusted user accounts from being activated via API.
# They might be activated via Admin or programmatically.
if not user.trust_bridge.is_trusted:
raise MissingUserAttribute("User has no trusted trust bridge.")
user.create_ldap_account(serializer.validated_data["password"]) user.create_ldap_account(serializer.validated_data["password"])
except (MissingUserAttribute, PasswordMismatch) as e: except (MissingUserAttribute, PasswordMismatch) as e:
return Response({"message": str(e)}, status=status.HTTP_400_BAD_REQUEST) return Response({"message": str(e)}, status=status.HTTP_400_BAD_REQUEST)