aa44cf473bec2101670bd3c7246b2e26ce45058f
[anni] / test / pleroma / web / pleroma_api / controllers / two_factor_authentication_controller_test.exs
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.TwoFactorAuthenticationControllerTest do
6   use Pleroma.Web.ConnCase, async: true
7
8   import Pleroma.Factory
9   alias Pleroma.MFA.Settings
10   alias Pleroma.MFA.TOTP
11
12   describe "GET /api/pleroma/accounts/mfa/settings" do
13     test "returns user mfa settings for new user", %{conn: conn} do
14       token = insert(:oauth_token, scopes: ["read", "follow"])
15       token2 = insert(:oauth_token, scopes: ["write"])
16
17       assert conn
18              |> put_req_header("authorization", "Bearer #{token.token}")
19              |> get("/api/pleroma/accounts/mfa")
20              |> json_response(:ok) == %{
21                "settings" => %{"enabled" => false, "totp" => false}
22              }
23
24       assert conn
25              |> put_req_header("authorization", "Bearer #{token2.token}")
26              |> get("/api/pleroma/accounts/mfa")
27              |> json_response(403) == %{
28                "error" => "Insufficient permissions: read:security."
29              }
30     end
31
32     test "returns user mfa settings with enabled totp", %{conn: conn} do
33       user =
34         insert(:user,
35           multi_factor_authentication_settings: %Settings{
36             enabled: true,
37             totp: %Settings.TOTP{secret: "XXX", delivery_type: "app", confirmed: true}
38           }
39         )
40
41       token = insert(:oauth_token, scopes: ["read", "follow"], user: user)
42
43       assert conn
44              |> put_req_header("authorization", "Bearer #{token.token}")
45              |> get("/api/pleroma/accounts/mfa")
46              |> json_response(:ok) == %{
47                "settings" => %{"enabled" => true, "totp" => true}
48              }
49     end
50   end
51
52   describe "GET /api/pleroma/accounts/mfa/backup_codes" do
53     test "returns backup codes", %{conn: conn} do
54       user =
55         insert(:user,
56           multi_factor_authentication_settings: %Settings{
57             backup_codes: ["1", "2", "3"],
58             totp: %Settings.TOTP{secret: "secret"}
59           }
60         )
61
62       token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
63       token2 = insert(:oauth_token, scopes: ["read"])
64
65       response =
66         conn
67         |> put_req_header("authorization", "Bearer #{token.token}")
68         |> get("/api/pleroma/accounts/mfa/backup_codes")
69         |> json_response(:ok)
70
71       assert [<<_::bytes-size(6)>>, <<_::bytes-size(6)>>] = response["codes"]
72       user = refresh_record(user)
73       mfa_settings = user.multi_factor_authentication_settings
74       assert mfa_settings.totp.secret == "secret"
75       refute mfa_settings.backup_codes == ["1", "2", "3"]
76       refute mfa_settings.backup_codes == []
77
78       assert conn
79              |> put_req_header("authorization", "Bearer #{token2.token}")
80              |> get("/api/pleroma/accounts/mfa/backup_codes")
81              |> json_response(403) == %{
82                "error" => "Insufficient permissions: write:security."
83              }
84     end
85   end
86
87   describe "GET /api/pleroma/accounts/mfa/setup/totp" do
88     test "return errors when method is invalid", %{conn: conn} do
89       user = insert(:user)
90       token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
91
92       response =
93         conn
94         |> put_req_header("authorization", "Bearer #{token.token}")
95         |> get("/api/pleroma/accounts/mfa/setup/torf")
96         |> json_response(400)
97
98       assert response == %{"error" => "undefined method"}
99     end
100
101     test "returns key and provisioning_uri", %{conn: conn} do
102       user =
103         insert(:user,
104           multi_factor_authentication_settings: %Settings{backup_codes: ["1", "2", "3"]}
105         )
106
107       token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
108       token2 = insert(:oauth_token, scopes: ["read"])
109
110       response =
111         conn
112         |> put_req_header("authorization", "Bearer #{token.token}")
113         |> get("/api/pleroma/accounts/mfa/setup/totp")
114         |> json_response(:ok)
115
116       user = refresh_record(user)
117       mfa_settings = user.multi_factor_authentication_settings
118       secret = mfa_settings.totp.secret
119       refute mfa_settings.enabled
120       assert mfa_settings.backup_codes == ["1", "2", "3"]
121
122       assert response == %{
123                "key" => secret,
124                "provisioning_uri" => TOTP.provisioning_uri(secret, "#{user.email}")
125              }
126
127       assert conn
128              |> put_req_header("authorization", "Bearer #{token2.token}")
129              |> get("/api/pleroma/accounts/mfa/setup/totp")
130              |> json_response(403) == %{
131                "error" => "Insufficient permissions: write:security."
132              }
133     end
134   end
135
136   describe "GET /api/pleroma/accounts/mfa/confirm/totp" do
137     test "returns success result", %{conn: conn} do
138       secret = TOTP.generate_secret()
139       code = TOTP.generate_token(secret)
140
141       user =
142         insert(:user,
143           multi_factor_authentication_settings: %Settings{
144             backup_codes: ["1", "2", "3"],
145             totp: %Settings.TOTP{secret: secret}
146           }
147         )
148
149       token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
150       token2 = insert(:oauth_token, scopes: ["read"])
151
152       assert conn
153              |> put_req_header("authorization", "Bearer #{token.token}")
154              |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code})
155              |> json_response(:ok)
156
157       settings = refresh_record(user).multi_factor_authentication_settings
158       assert settings.enabled
159       assert settings.totp.secret == secret
160       assert settings.totp.confirmed
161       assert settings.backup_codes == ["1", "2", "3"]
162
163       assert conn
164              |> put_req_header("authorization", "Bearer #{token2.token}")
165              |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code})
166              |> json_response(403) == %{
167                "error" => "Insufficient permissions: write:security."
168              }
169     end
170
171     test "returns error if password incorrect", %{conn: conn} do
172       secret = TOTP.generate_secret()
173       code = TOTP.generate_token(secret)
174
175       user =
176         insert(:user,
177           multi_factor_authentication_settings: %Settings{
178             backup_codes: ["1", "2", "3"],
179             totp: %Settings.TOTP{secret: secret}
180           }
181         )
182
183       token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
184
185       response =
186         conn
187         |> put_req_header("authorization", "Bearer #{token.token}")
188         |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "xxx", code: code})
189         |> json_response(422)
190
191       settings = refresh_record(user).multi_factor_authentication_settings
192       refute settings.enabled
193       refute settings.totp.confirmed
194       assert settings.backup_codes == ["1", "2", "3"]
195       assert response == %{"error" => "Invalid password."}
196     end
197
198     test "returns error if code incorrect", %{conn: conn} do
199       secret = TOTP.generate_secret()
200
201       user =
202         insert(:user,
203           multi_factor_authentication_settings: %Settings{
204             backup_codes: ["1", "2", "3"],
205             totp: %Settings.TOTP{secret: secret}
206           }
207         )
208
209       token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
210       token2 = insert(:oauth_token, scopes: ["read"])
211
212       response =
213         conn
214         |> put_req_header("authorization", "Bearer #{token.token}")
215         |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"})
216         |> json_response(422)
217
218       settings = refresh_record(user).multi_factor_authentication_settings
219       refute settings.enabled
220       refute settings.totp.confirmed
221       assert settings.backup_codes == ["1", "2", "3"]
222       assert response == %{"error" => "invalid_token"}
223
224       assert conn
225              |> put_req_header("authorization", "Bearer #{token2.token}")
226              |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"})
227              |> json_response(403) == %{
228                "error" => "Insufficient permissions: write:security."
229              }
230     end
231   end
232
233   describe "DELETE /api/pleroma/accounts/mfa/totp" do
234     test "returns success result", %{conn: conn} do
235       user =
236         insert(:user,
237           multi_factor_authentication_settings: %Settings{
238             backup_codes: ["1", "2", "3"],
239             totp: %Settings.TOTP{secret: "secret"}
240           }
241         )
242
243       token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
244       token2 = insert(:oauth_token, scopes: ["read"])
245
246       assert conn
247              |> put_req_header("authorization", "Bearer #{token.token}")
248              |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"})
249              |> json_response(:ok)
250
251       settings = refresh_record(user).multi_factor_authentication_settings
252       refute settings.enabled
253       assert settings.totp.secret == nil
254       refute settings.totp.confirmed
255
256       assert conn
257              |> put_req_header("authorization", "Bearer #{token2.token}")
258              |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"})
259              |> json_response(403) == %{
260                "error" => "Insufficient permissions: write:security."
261              }
262     end
263   end
264 end