From 3e22740eacc7c35470d08ee6ae7a451821c5b176 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Mon, 3 Jan 2022 13:30:39 +0100 Subject: [PATCH] core: add API endpoint to directly set user's password closes #2040 Signed-off-by: Jens Langhammer --- authentik/core/api/users.py | 30 ++++++++++++ authentik/core/tests/test_users_api.py | 13 +++++ locale/en/LC_MESSAGES/django.po | 48 +++++++++---------- schema.yml | 36 ++++++++++++++ web/src/locales/en.po | 22 ++++++++- web/src/locales/fr_FR.po | 22 ++++++++- web/src/locales/pseudo-LOCALE.po | 20 +++++++- web/src/locales/tr.po | 22 ++++++++- .../providers/proxy/ProxyProviderForm.ts | 2 +- web/src/pages/users/UserListPage.ts | 18 +++++-- web/src/pages/users/UserPasswordForm.ts | 36 ++++++++++++++ 11 files changed, 234 insertions(+), 35 deletions(-) create mode 100644 web/src/pages/users/UserPasswordForm.ts diff --git a/authentik/core/api/users.py b/authentik/core/api/users.py index f5dd9c290..948484a31 100644 --- a/authentik/core/api/users.py +++ b/authentik/core/api/users.py @@ -3,6 +3,7 @@ from datetime import timedelta from json import loads from typing import Optional +from django.contrib.auth import update_session_auth_hash from django.db.models.query import QuerySet from django.db.transaction import atomic from django.db.utils import IntegrityError @@ -359,6 +360,35 @@ class UserViewSet(UsedByMixin, ModelViewSet): ).data return Response(serializer.initial_data) + @permission_required("authentik_core.reset_user_password") + @extend_schema( + request=inline_serializer( + "UserPasswordSetSerializer", + { + "password": CharField(required=True), + }, + ), + responses={ + 204: "", + 400: "", + }, + ) + @action(detail=True, methods=["POST"]) + # pylint: disable=invalid-name, unused-argument + def set_password(self, request: Request, pk: int) -> Response: + """Set password for user""" + user: User = self.get_object() + try: + user.set_password(request.data.get("password")) + user.save() + except (ValidationError, IntegrityError) as exc: + LOGGER.debug("Failed to set password", exc=exc) + return Response(status=400) + if user.pk == request.user.pk and SESSION_IMPERSONATE_USER not in self.request.session: + LOGGER.debug("Updating session hash after password change") + update_session_auth_hash(self.request, user) + return Response(status=204) + @extend_schema(request=UserSelfSerializer, responses={200: SessionUserSerializer(many=False)}) @action( methods=["PUT"], diff --git a/authentik/core/tests/test_users_api.py b/authentik/core/tests/test_users_api.py index 49a794960..ed2d4f525 100644 --- a/authentik/core/tests/test_users_api.py +++ b/authentik/core/tests/test_users_api.py @@ -5,6 +5,7 @@ from rest_framework.test import APITestCase from authentik.core.models import USER_ATTRIBUTE_CHANGE_EMAIL, USER_ATTRIBUTE_CHANGE_USERNAME, User from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_tenant from authentik.flows.models import FlowDesignation +from authentik.lib.generators import generate_key from authentik.stages.email.models import EmailStage from authentik.tenants.models import Tenant @@ -68,6 +69,18 @@ class TestUsersAPI(APITestCase): ) self.assertEqual(response.status_code, 404) + def test_set_password(self): + """Test Direct password set""" + self.client.force_login(self.admin) + new_pw = generate_key() + response = self.client.post( + reverse("authentik_api:user-set-password", kwargs={"pk": self.admin.pk}), + data={"password": new_pw}, + ) + self.assertEqual(response.status_code, 204) + self.admin.refresh_from_db() + self.assertTrue(self.admin.check_password(new_pw)) + def test_recovery(self): """Test user recovery link (no recovery flow set)""" flow = create_test_flow(FlowDesignation.RECOVERY) diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 8523c4372..f68db8f8e 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-01-01 14:02+0000\n" +"POT-Creation-Date: 2022-01-03 12:29+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -39,99 +39,99 @@ msgstr "" msgid "Create a SAML Provider by importing its Metadata." msgstr "" -#: authentik/core/models.py:68 +#: authentik/core/models.py:69 msgid "name" msgstr "" -#: authentik/core/models.py:70 +#: authentik/core/models.py:71 msgid "Users added to this group will be superusers." msgstr "" -#: authentik/core/models.py:128 +#: authentik/core/models.py:129 msgid "User's display name." msgstr "" -#: authentik/core/models.py:195 authentik/providers/oauth2/models.py:299 +#: authentik/core/models.py:212 authentik/providers/oauth2/models.py:299 msgid "User" msgstr "" -#: authentik/core/models.py:196 +#: authentik/core/models.py:213 msgid "Users" msgstr "" -#: authentik/core/models.py:207 +#: authentik/core/models.py:224 msgid "Flow used when authorizing this provider." msgstr "" -#: authentik/core/models.py:240 +#: authentik/core/models.py:257 msgid "Application's display Name." msgstr "" -#: authentik/core/models.py:241 +#: authentik/core/models.py:258 msgid "Internal application name, used in URLs." msgstr "" -#: authentik/core/models.py:294 +#: authentik/core/models.py:311 msgid "Application" msgstr "" -#: authentik/core/models.py:295 +#: authentik/core/models.py:312 msgid "Applications" msgstr "" -#: authentik/core/models.py:301 +#: authentik/core/models.py:318 msgid "Use the source-specific identifier" msgstr "" -#: authentik/core/models.py:309 +#: authentik/core/models.py:326 msgid "" "Use the user's email address, but deny enrollment when the email address " "already exists." msgstr "" -#: authentik/core/models.py:318 +#: authentik/core/models.py:335 msgid "" "Use the user's username, but deny enrollment when the username already " "exists." msgstr "" -#: authentik/core/models.py:325 +#: authentik/core/models.py:342 msgid "Source's display Name." msgstr "" -#: authentik/core/models.py:326 +#: authentik/core/models.py:343 msgid "Internal source name, used in URLs." msgstr "" -#: authentik/core/models.py:337 +#: authentik/core/models.py:354 msgid "Flow to use when authenticating existing users." msgstr "" -#: authentik/core/models.py:346 +#: authentik/core/models.py:363 msgid "Flow to use when enrolling new users." msgstr "" -#: authentik/core/models.py:484 +#: authentik/core/models.py:501 msgid "Token" msgstr "" -#: authentik/core/models.py:485 +#: authentik/core/models.py:502 msgid "Tokens" msgstr "" -#: authentik/core/models.py:528 +#: authentik/core/models.py:545 msgid "Property Mapping" msgstr "" -#: authentik/core/models.py:529 +#: authentik/core/models.py:546 msgid "Property Mappings" msgstr "" -#: authentik/core/models.py:565 +#: authentik/core/models.py:582 msgid "Authenticated Session" msgstr "" -#: authentik/core/models.py:566 +#: authentik/core/models.py:583 msgid "Authenticated Sessions" msgstr "" diff --git a/schema.yml b/schema.yml index 53d51b0bf..f71cbfbf0 100644 --- a/schema.yml +++ b/schema.yml @@ -3269,6 +3269,34 @@ paths: $ref: '#/components/schemas/ValidationError' '403': $ref: '#/components/schemas/GenericError' + /core/users/{id}/set_password/: + post: + operationId: core_users_set_password_create + description: Set password for user + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this User. + required: true + tags: + - core + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserPasswordSetRequest' + required: true + security: + - authentik: [] + responses: + '204': + description: No response body + '400': + description: No response body + '403': + $ref: '#/components/schemas/GenericError' /core/users/{id}/used_by/: get: operationId: core_users_used_by_list @@ -31091,6 +31119,14 @@ components: - identifier - source - user + UserPasswordSetRequest: + type: object + properties: + password: + type: string + minLength: 1 + required: + - password UserReputation: type: object description: UserReputation Serializer diff --git a/web/src/locales/en.po b/web/src/locales/en.po index 520fd5041..fdc85fdae 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -3441,6 +3441,7 @@ msgstr "Passing" #: src/flows/stages/identification/IdentificationStage.ts #: src/flows/stages/password/PasswordStage.ts #: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserPasswordForm.ts msgid "Password" msgstr "Password" @@ -4259,6 +4260,10 @@ msgstr "Set a custom HTTP-Basic Authentication header based on values from authe msgid "Set custom attributes using YAML or JSON." msgstr "Set custom attributes using YAML or JSON." +#: src/pages/users/UserListPage.ts +msgid "Set password" +msgstr "Set password" + #: src/pages/providers/proxy/ProxyProviderForm.ts msgid "Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." msgstr "Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." @@ -4765,6 +4770,10 @@ msgstr "Successfully updated mapping." msgid "Successfully updated outpost." msgstr "Successfully updated outpost." +#: src/pages/users/UserPasswordForm.ts +msgid "Successfully updated password." +msgstr "Successfully updated password." + #: src/pages/policies/dummy/DummyPolicyForm.ts #: src/pages/policies/event_matcher/EventMatcherPolicyForm.ts #: src/pages/policies/expiry/ExpiryPolicyForm.ts @@ -5155,8 +5164,12 @@ msgid "To create a recovery link, the current tenant needs to have a recovery fl msgstr "To create a recovery link, the current tenant needs to have a recovery flow configured." #: src/pages/users/UserListPage.ts -msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant." -msgstr "To directly reset a user's password, configure a recovery flow on the currently active tenant." +#~ msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant." +#~ msgstr "To directly reset a user's password, configure a recovery flow on the currently active tenant." + +#: src/pages/users/UserListPage.ts +msgid "To let a user directly reset a their password, configure a recovery flow on the currently active tenant." +msgstr "To let a user directly reset a their password, configure a recovery flow on the currently active tenant." #: src/pages/sources/ldap/LDAPSourceForm.ts msgid "To use SSL instead, use 'ldaps://' and disable this option." @@ -5520,6 +5533,11 @@ msgstr "Update available" msgid "Update details" msgstr "Update details" +#: src/pages/users/UserListPage.ts +#: src/pages/users/UserListPage.ts +msgid "Update password" +msgstr "Update password" + #: src/pages/flows/BoundStagesList.ts #: src/pages/outposts/ServiceConnectionListPage.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/fr_FR.po b/web/src/locales/fr_FR.po index 9c8ed397a..b7a1a17f6 100644 --- a/web/src/locales/fr_FR.po +++ b/web/src/locales/fr_FR.po @@ -3414,6 +3414,7 @@ msgstr "Réussite" #: src/flows/stages/identification/IdentificationStage.ts #: src/flows/stages/password/PasswordStage.ts #: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserPasswordForm.ts msgid "Password" msgstr "Mot de passe" @@ -4223,6 +4224,10 @@ msgstr "Définir un en-tête d'authentification HTTP-Basic personnalisé basé s msgid "Set custom attributes using YAML or JSON." msgstr "Définissez des attributs personnalisés via YAML ou JSON." +#: src/pages/users/UserListPage.ts +msgid "Set password" +msgstr "" + #: src/pages/providers/proxy/ProxyProviderForm.ts msgid "Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." msgstr "" @@ -4724,6 +4729,10 @@ msgstr "Mapping mis à jour avec succès" msgid "Successfully updated outpost." msgstr "Avant-poste mis à jour avec succès" +#: src/pages/users/UserPasswordForm.ts +msgid "Successfully updated password." +msgstr "" + #: src/pages/policies/dummy/DummyPolicyForm.ts #: src/pages/policies/event_matcher/EventMatcherPolicyForm.ts #: src/pages/policies/expiry/ExpiryPolicyForm.ts @@ -5099,8 +5108,12 @@ msgid "To create a recovery link, the current tenant needs to have a recovery fl msgstr "Pour créer un lien de récupération, le locataire actuel doit avoir un flux de récupération configuré." #: src/pages/users/UserListPage.ts -msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant." -msgstr "Pour réinitialiser directement le mot de passe d'un utilisateur, configurez un flux de récupération sur le locataire actuel." +#~ msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant." +#~ msgstr "Pour réinitialiser directement le mot de passe d'un utilisateur, configurez un flux de récupération sur le locataire actuel." + +#: src/pages/users/UserListPage.ts +msgid "To let a user directly reset a their password, configure a recovery flow on the currently active tenant." +msgstr "" #: src/pages/sources/ldap/LDAPSourceForm.ts msgid "To use SSL instead, use 'ldaps://' and disable this option." @@ -5461,6 +5474,11 @@ msgstr "Mise à jour disponibl" msgid "Update details" msgstr "Détails de la mise à jour" +#: src/pages/users/UserListPage.ts +#: src/pages/users/UserListPage.ts +msgid "Update password" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/outposts/ServiceConnectionListPage.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index b9a5ff221..cb2dfd868 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -3431,6 +3431,7 @@ msgstr "" #: src/flows/stages/identification/IdentificationStage.ts #: src/flows/stages/password/PasswordStage.ts #: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserPasswordForm.ts msgid "Password" msgstr "" @@ -4249,6 +4250,10 @@ msgstr "" msgid "Set custom attributes using YAML or JSON." msgstr "" +#: src/pages/users/UserListPage.ts +msgid "Set password" +msgstr "" + #: src/pages/providers/proxy/ProxyProviderForm.ts msgid "Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." msgstr "" @@ -4755,6 +4760,10 @@ msgstr "" msgid "Successfully updated outpost." msgstr "" +#: src/pages/users/UserPasswordForm.ts +msgid "Successfully updated password." +msgstr "" + #: src/pages/policies/dummy/DummyPolicyForm.ts #: src/pages/policies/event_matcher/EventMatcherPolicyForm.ts #: src/pages/policies/expiry/ExpiryPolicyForm.ts @@ -5135,7 +5144,11 @@ msgid "To create a recovery link, the current tenant needs to have a recovery fl msgstr "" #: src/pages/users/UserListPage.ts -msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant." +#~ msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant." +#~ msgstr "" + +#: src/pages/users/UserListPage.ts +msgid "To let a user directly reset a their password, configure a recovery flow on the currently active tenant." msgstr "" #: src/pages/sources/ldap/LDAPSourceForm.ts @@ -5500,6 +5513,11 @@ msgstr "" msgid "Update details" msgstr "" +#: src/pages/users/UserListPage.ts +#: src/pages/users/UserListPage.ts +msgid "Update password" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/outposts/ServiceConnectionListPage.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/tr.po b/web/src/locales/tr.po index 87ad54a7d..6f5110e37 100644 --- a/web/src/locales/tr.po +++ b/web/src/locales/tr.po @@ -3383,6 +3383,7 @@ msgstr "Geçiyor" #: src/flows/stages/identification/IdentificationStage.ts #: src/flows/stages/password/PasswordStage.ts #: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserPasswordForm.ts msgid "Password" msgstr "Parola" @@ -4182,6 +4183,10 @@ msgstr "authentik değerlerine göre özel bir HTTP-Basic Kimlik Doğrulama baş msgid "Set custom attributes using YAML or JSON." msgstr "YAML veya JSON kullanarak özel nitelikleri ayarlayın." +#: src/pages/users/UserListPage.ts +msgid "Set password" +msgstr "" + #: src/pages/providers/proxy/ProxyProviderForm.ts msgid "Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." msgstr "Bunu kimlik doğrulamasının geçerli olmasını istediğiniz etki alanına ayarlayın. Yukarıdaki URL'nin bir üst etki alanı olmalıdır. Uygulamaları app1.domain.tld, app2.domain.tld olarak çalıştırıyorsanız, bunu 'domain.tld' olarak ayarlayın." @@ -4671,6 +4676,10 @@ msgstr "Eşleme başarıyla güncellendi." msgid "Successfully updated outpost." msgstr "İleri üssü başarıyla güncelledi." +#: src/pages/users/UserPasswordForm.ts +msgid "Successfully updated password." +msgstr "" + #: src/pages/policies/dummy/DummyPolicyForm.ts #: src/pages/policies/event_matcher/EventMatcherPolicyForm.ts #: src/pages/policies/expiry/ExpiryPolicyForm.ts @@ -5050,8 +5059,12 @@ msgid "To create a recovery link, the current tenant needs to have a recovery fl msgstr "Kurtarma bağlantısı oluşturmak için geçerli sakinin yapılandırılmış bir kurtarma akışı olması gerekir." #: src/pages/users/UserListPage.ts -msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant." -msgstr "Bir kullanıcının parolasını doğrudan sıfırlamak için, etkin olan sakin üzerinde bir kurtarma akışı yapılandırın." +#~ msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant." +#~ msgstr "Bir kullanıcının parolasını doğrudan sıfırlamak için, etkin olan sakin üzerinde bir kurtarma akışı yapılandırın." + +#: src/pages/users/UserListPage.ts +msgid "To let a user directly reset a their password, configure a recovery flow on the currently active tenant." +msgstr "" #: src/pages/sources/ldap/LDAPSourceForm.ts msgid "To use SSL instead, use 'ldaps://' and disable this option." @@ -5412,6 +5425,11 @@ msgstr "Güncelleme mevcut" msgid "Update details" msgstr "Ayrıntıları güncelle" +#: src/pages/users/UserListPage.ts +#: src/pages/users/UserListPage.ts +msgid "Update password" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/outposts/ServiceConnectionListPage.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/pages/providers/proxy/ProxyProviderForm.ts b/web/src/pages/providers/proxy/ProxyProviderForm.ts index 836914607..0f920e679 100644 --- a/web/src/pages/providers/proxy/ProxyProviderForm.ts +++ b/web/src/pages/providers/proxy/ProxyProviderForm.ts @@ -404,7 +404,7 @@ export class ProxyProviderFormPage extends ModelForm {