move to 2.5.5
[anni] / lib / pleroma / web / pleroma_api / controllers / two_factor_authentication_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do
6   @moduledoc "The module represents actions to manage MFA"
7   use Pleroma.Web, :controller
8
9   import Pleroma.Web.ControllerHelper, only: [json_response: 3]
10
11   alias Pleroma.MFA
12   alias Pleroma.MFA.TOTP
13   alias Pleroma.Web.CommonAPI.Utils
14   alias Pleroma.Web.Plugs.OAuthScopesPlug
15
16   plug(OAuthScopesPlug, %{scopes: ["read:security"]} when action in [:settings])
17
18   plug(
19     OAuthScopesPlug,
20     %{scopes: ["write:security"]} when action in [:setup, :confirm, :disable, :backup_codes]
21   )
22
23   @doc """
24   Gets user multi factor authentication settings
25
26   ## Endpoint
27   GET /api/pleroma/accounts/mfa
28
29   """
30   def settings(%{assigns: %{user: user}} = conn, _params) do
31     json(conn, %{settings: MFA.mfa_settings(user)})
32   end
33
34   @doc """
35   Prepare setup mfa method
36
37   ## Endpoint
38   GET /api/pleroma/accounts/mfa/setup/[:method]
39
40   """
41   def setup(%{assigns: %{user: user}} = conn, %{"method" => "totp"} = _params) do
42     with {:ok, user} <- MFA.setup_totp(user),
43          %{secret: secret} = _ <- user.multi_factor_authentication_settings.totp do
44       provisioning_uri = TOTP.provisioning_uri(secret, "#{user.email}")
45
46       json(conn, %{provisioning_uri: provisioning_uri, key: secret})
47     else
48       {:error, message} ->
49         json_response(conn, :unprocessable_entity, %{error: message})
50     end
51   end
52
53   def setup(conn, _params) do
54     json_response(conn, :bad_request, %{error: "undefined method"})
55   end
56
57   @doc """
58   Confirms setup and enable mfa method
59
60   ## Endpoint
61   POST /api/pleroma/accounts/mfa/confirm/:method
62
63   - params:
64   `code` - confirmation code
65   `password` - current password
66   """
67   def confirm(
68         %{assigns: %{user: user}} = conn,
69         %{"method" => "totp", "password" => _, "code" => _} = params
70       ) do
71     with {:ok, _user} <- Utils.confirm_current_password(user, params["password"]),
72          {:ok, _user} <- MFA.confirm_totp(user, params) do
73       json(conn, %{})
74     else
75       {:error, message} ->
76         json_response(conn, :unprocessable_entity, %{error: message})
77     end
78   end
79
80   def confirm(conn, _) do
81     json_response(conn, :bad_request, %{error: "undefined mfa method"})
82   end
83
84   @doc """
85   Disable mfa method and disable mfa if need.
86   """
87   def disable(%{assigns: %{user: user}} = conn, %{"method" => "totp"} = params) do
88     with {:ok, user} <- Utils.confirm_current_password(user, params["password"]),
89          {:ok, _user} <- MFA.disable_totp(user) do
90       json(conn, %{})
91     else
92       {:error, message} ->
93         json_response(conn, :unprocessable_entity, %{error: message})
94     end
95   end
96
97   def disable(%{assigns: %{user: user}} = conn, %{"method" => "mfa"} = params) do
98     with {:ok, user} <- Utils.confirm_current_password(user, params["password"]),
99          {:ok, _user} <- MFA.disable(user) do
100       json(conn, %{})
101     else
102       {:error, message} ->
103         json_response(conn, :unprocessable_entity, %{error: message})
104     end
105   end
106
107   def disable(conn, _) do
108     json_response(conn, :bad_request, %{error: "undefined mfa method"})
109   end
110
111   @doc """
112   Generates backup codes.
113
114   ## Endpoint
115   GET /api/pleroma/accounts/mfa/backup_codes
116
117   ## Response
118   ### Success
119   `{codes: [codes]}`
120
121   ### Error
122   `{error: [error_message]}`
123
124   """
125   def backup_codes(%{assigns: %{user: user}} = conn, _params) do
126     with {:ok, codes} <- MFA.generate_backup_codes(user) do
127       json(conn, %{codes: codes})
128     else
129       {:error, message} ->
130         json_response(conn, :unprocessable_entity, %{error: message})
131     end
132   end
133 end