Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
6.9KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Plugs.OAuthScopesPlugTest do
  5. use Pleroma.Web.ConnCase, async: true
  6. alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
  7. alias Pleroma.Plugs.OAuthScopesPlug
  8. alias Pleroma.Repo
  9. import Mock
  10. import Pleroma.Factory
  11. setup_with_mocks([{EnsurePublicOrAuthenticatedPlug, [], [call: fn conn, _ -> conn end]}]) do
  12. :ok
  13. end
  14. describe "when `assigns[:token]` is nil, " do
  15. test "with :skip_instance_privacy_check option, proceeds with no op", %{conn: conn} do
  16. conn =
  17. conn
  18. |> assign(:user, insert(:user))
  19. |> OAuthScopesPlug.call(%{scopes: ["read"], skip_instance_privacy_check: true})
  20. refute conn.halted
  21. assert conn.assigns[:user]
  22. refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_))
  23. end
  24. test "without :skip_instance_privacy_check option, calls EnsurePublicOrAuthenticatedPlug", %{
  25. conn: conn
  26. } do
  27. conn =
  28. conn
  29. |> assign(:user, insert(:user))
  30. |> OAuthScopesPlug.call(%{scopes: ["read"]})
  31. refute conn.halted
  32. assert conn.assigns[:user]
  33. assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_))
  34. end
  35. end
  36. test "if `token.scopes` fulfills specified 'any of' conditions, " <>
  37. "proceeds with no op",
  38. %{conn: conn} do
  39. token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user)
  40. conn =
  41. conn
  42. |> assign(:user, token.user)
  43. |> assign(:token, token)
  44. |> OAuthScopesPlug.call(%{scopes: ["read"]})
  45. refute conn.halted
  46. assert conn.assigns[:user]
  47. end
  48. test "if `token.scopes` fulfills specified 'all of' conditions, " <>
  49. "proceeds with no op",
  50. %{conn: conn} do
  51. token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user)
  52. conn =
  53. conn
  54. |> assign(:user, token.user)
  55. |> assign(:token, token)
  56. |> OAuthScopesPlug.call(%{scopes: ["scope2", "scope3"], op: :&})
  57. refute conn.halted
  58. assert conn.assigns[:user]
  59. end
  60. describe "with `fallback: :proceed_unauthenticated` option, " do
  61. test "if `token.scopes` doesn't fulfill specified 'any of' conditions, " <>
  62. "clears `assigns[:user]` and calls EnsurePublicOrAuthenticatedPlug",
  63. %{conn: conn} do
  64. token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user)
  65. conn =
  66. conn
  67. |> assign(:user, token.user)
  68. |> assign(:token, token)
  69. |> OAuthScopesPlug.call(%{scopes: ["follow"], fallback: :proceed_unauthenticated})
  70. refute conn.halted
  71. refute conn.assigns[:user]
  72. assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_))
  73. end
  74. test "if `token.scopes` doesn't fulfill specified 'all of' conditions, " <>
  75. "clears `assigns[:user] and calls EnsurePublicOrAuthenticatedPlug",
  76. %{conn: conn} do
  77. token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user)
  78. conn =
  79. conn
  80. |> assign(:user, token.user)
  81. |> assign(:token, token)
  82. |> OAuthScopesPlug.call(%{
  83. scopes: ["read", "follow"],
  84. op: :&,
  85. fallback: :proceed_unauthenticated
  86. })
  87. refute conn.halted
  88. refute conn.assigns[:user]
  89. assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_))
  90. end
  91. test "with :skip_instance_privacy_check option, " <>
  92. "if `token.scopes` doesn't fulfill specified conditions, " <>
  93. "clears `assigns[:user]` and does not call EnsurePublicOrAuthenticatedPlug",
  94. %{conn: conn} do
  95. token = insert(:oauth_token, scopes: ["read:statuses", "write"]) |> Repo.preload(:user)
  96. conn =
  97. conn
  98. |> assign(:user, token.user)
  99. |> assign(:token, token)
  100. |> OAuthScopesPlug.call(%{
  101. scopes: ["read"],
  102. fallback: :proceed_unauthenticated,
  103. skip_instance_privacy_check: true
  104. })
  105. refute conn.halted
  106. refute conn.assigns[:user]
  107. refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_))
  108. end
  109. end
  110. describe "without :fallback option, " do
  111. test "if `token.scopes` does not fulfill specified 'any of' conditions, " <>
  112. "returns 403 and halts",
  113. %{conn: conn} do
  114. token = insert(:oauth_token, scopes: ["read", "write"])
  115. any_of_scopes = ["follow"]
  116. conn =
  117. conn
  118. |> assign(:token, token)
  119. |> OAuthScopesPlug.call(%{scopes: any_of_scopes})
  120. assert conn.halted
  121. assert 403 == conn.status
  122. expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, ", ")}."
  123. assert Jason.encode!(%{error: expected_error}) == conn.resp_body
  124. end
  125. test "if `token.scopes` does not fulfill specified 'all of' conditions, " <>
  126. "returns 403 and halts",
  127. %{conn: conn} do
  128. token = insert(:oauth_token, scopes: ["read", "write"])
  129. all_of_scopes = ["write", "follow"]
  130. conn =
  131. conn
  132. |> assign(:token, token)
  133. |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&})
  134. assert conn.halted
  135. assert 403 == conn.status
  136. expected_error =
  137. "Insufficient permissions: #{Enum.join(all_of_scopes -- token.scopes, ", ")}."
  138. assert Jason.encode!(%{error: expected_error}) == conn.resp_body
  139. end
  140. end
  141. describe "with hierarchical scopes, " do
  142. test "if `token.scopes` fulfills specified 'any of' conditions, " <>
  143. "proceeds with no op",
  144. %{conn: conn} do
  145. token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user)
  146. conn =
  147. conn
  148. |> assign(:user, token.user)
  149. |> assign(:token, token)
  150. |> OAuthScopesPlug.call(%{scopes: ["read:something"]})
  151. refute conn.halted
  152. assert conn.assigns[:user]
  153. end
  154. test "if `token.scopes` fulfills specified 'all of' conditions, " <>
  155. "proceeds with no op",
  156. %{conn: conn} do
  157. token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user)
  158. conn =
  159. conn
  160. |> assign(:user, token.user)
  161. |> assign(:token, token)
  162. |> OAuthScopesPlug.call(%{scopes: ["scope1:subscope", "scope2:subscope"], op: :&})
  163. refute conn.halted
  164. assert conn.assigns[:user]
  165. end
  166. end
  167. describe "filter_descendants/2" do
  168. test "filters scopes which directly match or are ancestors of supported scopes" do
  169. f = fn scopes, supported_scopes ->
  170. OAuthScopesPlug.filter_descendants(scopes, supported_scopes)
  171. end
  172. assert f.(["read", "follow"], ["write", "read"]) == ["read"]
  173. assert f.(["read", "write:something", "follow"], ["write", "read"]) ==
  174. ["read", "write:something"]
  175. assert f.(["admin:read"], ["write", "read"]) == []
  176. assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"]
  177. end
  178. end
  179. end