feat: Allow to activate trusted accounts only
This commit is contained in:
parent
0f1cd98a80
commit
d656370aef
5 changed files with 40 additions and 44 deletions
23
src/api.ts
23
src/api.ts
|
@ -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> {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Reference in a new issue