diff --git a/authentik/core/api/users.py b/authentik/core/api/users.py index a27204623..1099232d8 100644 --- a/authentik/core/api/users.py +++ b/authentik/core/api/users.py @@ -1,4 +1,5 @@ """User API Views""" +from django.http.response import Http404 from django.urls import reverse_lazy from django.utils.http import urlencode from drf_yasg.utils import swagger_auto_schema, swagger_serializer_method @@ -19,6 +20,7 @@ from authentik.core.middleware import ( ) from authentik.core.models import Token, TokenIntents, User from authentik.events.models import EventAction +from authentik.flows.models import Flow, FlowDesignation class UserSerializer(ModelSerializer): @@ -121,12 +123,16 @@ class UserViewSet(ModelViewSet): @permission_required("authentik_core.reset_user_password") @swagger_auto_schema( - responses={"200": LinkSerializer(many=False)}, + responses={"200": LinkSerializer(many=False), "404": "No recovery flow found."}, ) @action(detail=True, pagination_class=None, filter_backends=[]) # pylint: disable=invalid-name, unused-argument def recovery(self, request: Request, pk: int) -> Response: """Create a temporary link that a user can use to recover their accounts""" + # Check that there is a recovery flow, if not return an error + flow = Flow.with_policy(request, designation=FlowDesignation.RECOVERY) + if not flow: + raise Http404 user: User = self.get_object() token, __ = Token.objects.get_or_create( identifier=f"{user.uid}-password-reset", diff --git a/authentik/sources/oauth/tests/test_views.py b/authentik/sources/oauth/tests/test_views.py index 48a7ef188..ef4108e42 100644 --- a/authentik/sources/oauth/tests/test_views.py +++ b/authentik/sources/oauth/tests/test_views.py @@ -1,8 +1,8 @@ """OAuth Source tests""" -from authentik.sources.oauth.api.source import OAuthSourceSerializer from django.test import TestCase from django.urls import reverse +from authentik.sources.oauth.api.source import OAuthSourceSerializer from authentik.sources.oauth.models import OAuthSource @@ -21,20 +21,28 @@ class TestOAuthSource(TestCase): def test_api_validate(self): """Test API validation""" - self.assertTrue(OAuthSourceSerializer(data={ - "name": "foo", - "slug": "bar", - "provider_type": "google", - "consumer_key": "foo", - "consumer_secret": "foo", - }).is_valid()) - self.assertFalse(OAuthSourceSerializer(data={ - "name": "foo", - "slug": "bar", - "provider_type": "openid-connect", - "consumer_key": "foo", - "consumer_secret": "foo", - }).is_valid()) + self.assertTrue( + OAuthSourceSerializer( + data={ + "name": "foo", + "slug": "bar", + "provider_type": "google", + "consumer_key": "foo", + "consumer_secret": "foo", + } + ).is_valid() + ) + self.assertFalse( + OAuthSourceSerializer( + data={ + "name": "foo", + "slug": "bar", + "provider_type": "openid-connect", + "consumer_key": "foo", + "consumer_secret": "foo", + } + ).is_valid() + ) def test_source_redirect(self): """test redirect view""" diff --git a/swagger.yaml b/swagger.yaml index 7425bdce5..58d1c47c5 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -2237,15 +2237,15 @@ paths: description: '' schema: $ref: '#/definitions/Link' - '403': - description: Authentication credentials were invalid, absent or insufficient. - schema: - $ref: '#/definitions/GenericError' '404': description: Object does not exist or caller has insufficient permissions to access it. schema: $ref: '#/definitions/APIException' + '403': + description: Authentication credentials were invalid, absent or insufficient. + schema: + $ref: '#/definitions/GenericError' tags: - core parameters: diff --git a/web/src/locales/en.po b/web/src/locales/en.po index 4d003c910..4d1ec403e 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -737,8 +737,8 @@ msgstr "Copy Key" #: src/pages/stages/prompt/PromptStageForm.ts:98 #: src/pages/user-settings/tokens/UserTokenList.ts:50 #: src/pages/user-settings/tokens/UserTokenList.ts:58 -#: src/pages/users/UserListPage.ts:144 -#: src/pages/users/UserListPage.ts:152 +#: src/pages/users/UserListPage.ts:151 +#: src/pages/users/UserListPage.ts:159 msgid "Create" msgstr "Create" @@ -808,7 +808,7 @@ msgstr "Create Stage binding" msgid "Create Token" msgstr "Create Token" -#: src/pages/users/UserListPage.ts:147 +#: src/pages/users/UserListPage.ts:154 msgid "Create User" msgstr "Create User" @@ -1531,7 +1531,7 @@ msgstr "If this flag is set, this Stage will jump to the next Stage when no Invi msgid "If your authentik Instance is using a self-signed certificate, set this value." msgstr "If your authentik Instance is using a self-signed certificate, set this value." -#: src/pages/users/UserListPage.ts:136 +#: src/pages/users/UserListPage.ts:143 msgid "Impersonate" msgstr "Impersonate" @@ -1996,6 +1996,10 @@ msgstr "No policies are currently bound to this object." msgid "No policies cached. Users may experience slow response times." msgstr "No policies cached. Users may experience slow response times." +#: src/pages/users/UserListPage.ts:135 +msgid "No recovery flow is configured." +msgstr "No recovery flow is configured." + #: src/pages/flows/BoundStagesList.ts:114 msgid "No stages are currently bound to this flow." msgstr "No stages are currently bound to this flow." @@ -2538,7 +2542,7 @@ msgstr "Required" msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." msgstr "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -#: src/pages/users/UserListPage.ts:133 +#: src/pages/users/UserListPage.ts:140 #: src/pages/users/UserViewPage.ts:165 msgid "Reset Password" msgstr "Reset Password" diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index ac28649e2..61e64a27b 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -731,8 +731,8 @@ msgstr "" #: src/pages/stages/prompt/PromptStageForm.ts:98 #: src/pages/user-settings/tokens/UserTokenList.ts:50 #: src/pages/user-settings/tokens/UserTokenList.ts:58 -#: src/pages/users/UserListPage.ts:144 -#: src/pages/users/UserListPage.ts:152 +#: src/pages/users/UserListPage.ts:151 +#: src/pages/users/UserListPage.ts:159 msgid "Create" msgstr "" @@ -802,7 +802,7 @@ msgstr "" msgid "Create Token" msgstr "" -#: src/pages/users/UserListPage.ts:147 +#: src/pages/users/UserListPage.ts:154 msgid "Create User" msgstr "" @@ -1523,7 +1523,7 @@ msgstr "" msgid "If your authentik Instance is using a self-signed certificate, set this value." msgstr "" -#: src/pages/users/UserListPage.ts:136 +#: src/pages/users/UserListPage.ts:143 msgid "Impersonate" msgstr "" @@ -1988,6 +1988,10 @@ msgstr "" msgid "No policies cached. Users may experience slow response times." msgstr "" +#: src/pages/users/UserListPage.ts:135 +msgid "No recovery flow is configured." +msgstr "" + #: src/pages/flows/BoundStagesList.ts:114 msgid "No stages are currently bound to this flow." msgstr "" @@ -2530,7 +2534,7 @@ msgstr "" msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." msgstr "" -#: src/pages/users/UserListPage.ts:133 +#: src/pages/users/UserListPage.ts:140 #: src/pages/users/UserViewPage.ts:165 msgid "Reset Password" msgstr "" diff --git a/web/src/pages/users/UserListPage.ts b/web/src/pages/users/UserListPage.ts index 43b060ea0..e3b2bbe38 100644 --- a/web/src/pages/users/UserListPage.ts +++ b/web/src/pages/users/UserListPage.ts @@ -127,6 +127,13 @@ export class UserListPage extends TablePage { message: t`Successfully generated recovery link`, description: rec.link }); + }).catch((ex: Response) => { + ex.json().then(() => { + showMessage({ + level: MessageLevel.error, + message: t`No recovery flow is configured.`, + }); + }); }); }}> ${t`Reset Password`}