From d656370aefd570012f342fcbd04c5bd7d6f7da03 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 3 Aug 2021 11:41:58 +0200 Subject: [PATCH] feat: Allow to activate trusted accounts only --- src/api.ts | 23 ++++++++++------------- src/components/UserTable.vue | 2 +- src/views/MainPage.vue | 16 +--------------- userausfall/rest_api/serializers.py | 23 +++++++++++------------ userausfall/rest_api/views.py | 20 +++++++++++++++++--- 5 files changed, 40 insertions(+), 44 deletions(-) diff --git a/src/api.ts b/src/api.ts index eea769e..59434b0 100644 --- a/src/api.ts +++ b/src/api.ts @@ -12,7 +12,7 @@ async function api_request( method: HTTPMethod, endpoint: string, successStatus: number, - data: any, + data?: any, authToken?: string ) { const init = { @@ -40,13 +40,15 @@ async function api_request( } export class User { - email: string | undefined; + pk: number | undefined; password: string | undefined; username: string | null = null; isAuthenticated = false; isTrusted = false; - private token = ""; + /** + * Activate the corresponding LDAP account. + */ async activate(): Promise { await api_request("POST", "users/activate", 204, { password: this.password, @@ -74,21 +76,16 @@ export class User { // successful logins are followed by a redirect if (res.redirected && res.status === 200) { this.isAuthenticated = true; + await this.retrieve(); } else { throw new APIError("", ""); } } - async save(): Promise { - await api_request( - "PATCH", - "users/me", - 200, - { - username: this.username, - }, - this.token - ); + async retrieve(): Promise { + const data = await api_request("GET", "users/me", 200); + this.pk = data.pk; + this.isTrusted = data.trust_bridge.is_trusted; } async signup(): Promise { diff --git a/src/components/UserTable.vue b/src/components/UserTable.vue index 2a98ae1..4da3984 100644 --- a/src/components/UserTable.vue +++ b/src/components/UserTable.vue @@ -2,7 +2,7 @@
Dein Konto ist noch nicht aktiv. - Jetzt aktivieren + Jetzt aktivieren diff --git a/src/views/MainPage.vue b/src/views/MainPage.vue index 274da2e..cf0509d 100644 --- a/src/views/MainPage.vue +++ b/src/views/MainPage.vue @@ -27,23 +27,9 @@ export default class Home extends mixins(NotifyMixin) { private user = new User(); public async created(): Promise { - if (this.$route.name === "confirm") { - await this.doConfirm(); - } else if (!this.user.isAuthenticated && this.$route.name !== "login") { + if (!this.user.isAuthenticated && this.$route.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(); - } - } } diff --git a/userausfall/rest_api/serializers.py b/userausfall/rest_api/serializers.py index b3d619d..4127b16 100644 --- a/userausfall/rest_api/serializers.py +++ b/userausfall/rest_api/serializers.py @@ -1,25 +1,24 @@ 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() -class UserSerializer(serializers.ModelSerializer): - confidant_email = serializers.EmailField() +class RetrieveUserSerializer(serializers.ModelSerializer): + trust_bridge = TrustBridgeSerializer(required=False, read_only=True) class Meta: model = User - fields = ("pk", "email", "username", "confidant_email") - 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) + fields = ["pk", "username", "trust_bridge"] class CreateUserSerializer(serializers.ModelSerializer): diff --git a/userausfall/rest_api/views.py b/userausfall/rest_api/views.py index ac65364..3768510 100644 --- a/userausfall/rest_api/views.py +++ b/userausfall/rest_api/views.py @@ -5,8 +5,11 @@ from rest_framework.response import Response from djeveric import ConfirmationView from userausfall.models import User, MissingUserAttribute, PasswordMismatch from userausfall.confirmations import ConfidantConfirmation -from userausfall.rest_api.permissions import UserPermission -from userausfall.rest_api.serializers import UserSerializer, UserActivationSerializer, CreateUserSerializer +from userausfall.rest_api.serializers import ( + ActivateUserSerializer, + CreateUserSerializer, + TrustBridgeSerializer, RetrieveUserSerializer, +) class ConfidantConfirmationView(ConfirmationView): @@ -17,13 +20,24 @@ class UserViewSet(viewsets.ModelViewSet): # permission_classes = [UserPermission] 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"]) def activate(self, request, pk=None): """Create the corresponding LDAP account.""" user: User = request.user # self.get_object() - serializer = UserActivationSerializer(data=request.data) + serializer = ActivateUserSerializer(data=request.data) if serializer.is_valid(): 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"]) except (MissingUserAttribute, PasswordMismatch) as e: return Response({"message": str(e)}, status=status.HTTP_400_BAD_REQUEST)