defmodule Pleroma.Web.OAuth.OAuthControllerTest do
use Pleroma.Web.ConnCase
import Pleroma.Factory
+ import Mock
+ alias Pleroma.Registration
alias Pleroma.Repo
alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.Token
- describe "GET /oauth/authorize" do
+ @session_opts [
+ store: :cookie,
+ key: "_test",
+ signing_salt: "cooldude"
+ ]
+
+ describe "in OAuth consumer mode, " do
setup do
- session_opts = [
- store: :cookie,
- key: "_test",
- signing_salt: "cooldude"
+ oauth_consumer_enabled_path = [:auth, :oauth_consumer_enabled]
+ oauth_consumer_strategies_path = [:auth, :oauth_consumer_strategies]
+ oauth_consumer_enabled = Pleroma.Config.get(oauth_consumer_enabled_path)
+ oauth_consumer_strategies = Pleroma.Config.get(oauth_consumer_strategies_path)
+
+ Pleroma.Config.put(oauth_consumer_enabled_path, true)
+ Pleroma.Config.put(oauth_consumer_strategies_path, ~w(twitter facebook))
+
+ on_exit(fn ->
+ Pleroma.Config.put(oauth_consumer_enabled_path, oauth_consumer_enabled)
+ Pleroma.Config.put(oauth_consumer_strategies_path, oauth_consumer_strategies)
+ end)
+
+ [
+ app: insert(:oauth_app),
+ conn:
+ build_conn()
+ |> Plug.Session.call(Plug.Session.init(@session_opts))
+ |> fetch_session()
]
+ end
+
+ test "GET /oauth/authorize also renders OAuth consumer form", %{
+ app: app,
+ conn: conn
+ } do
+ conn =
+ get(
+ conn,
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "scope" => "read"
+ }
+ )
+
+ assert response = html_response(conn, 200)
+ assert response =~ "Sign in with Twitter"
+ assert response =~ o_auth_path(conn, :prepare_request)
+ end
+
+ test "GET /oauth/prepare_request encodes parameters as `state` and redirects", %{
+ app: app,
+ conn: conn
+ } do
+ conn =
+ get(
+ conn,
+ "/oauth/prepare_request",
+ %{
+ "provider" => "twitter",
+ "scope" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state"
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ redirected_to = redirected_to(conn)
+ [state] = Regex.run(~r/(?<=state=).*?(?=\Z|&)/, redirected_to)
+ state = URI.decode(state)
+ assert {:ok, state_params} = Poison.decode(state)
+
+ expected_scope_param = Enum.join(app.scopes, "+")
+ expected_client_id_param = app.client_id
+ expected_redirect_uri_param = app.redirect_uris
+
+ assert %{
+ "scope" => ^expected_scope_param,
+ "client_id" => ^expected_client_id_param,
+ "redirect_uri" => ^expected_redirect_uri_param,
+ "state" => "a_state"
+ } = state_params
+ end
+
+ test "on authentication error, redirects to `redirect_uri`", %{app: app, conn: conn} do
+ state_params = %{
+ "scope" => Enum.join(app.scopes, " "),
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => ""
+ }
+
+ conn =
+ conn
+ |> assign(:ueberauth_failure, %{errors: [%{message: "unknown error"}]})
+ |> get(
+ "/oauth/twitter/callback",
+ %{
+ "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
+ "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
+ "provider" => "twitter",
+ "state" => Poison.encode!(state_params)
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) == app.redirect_uris
+ end
+
+ test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`",
+ %{app: app, conn: conn} do
+ registration = insert(:registration)
+
+ state_params = %{
+ "scope" => Enum.join(app.scopes, " "),
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => ""
+ }
+
+ with_mock Pleroma.Web.Auth.Authenticator,
+ get_registration: fn _, _ -> {:ok, registration} end do
+ conn =
+ get(
+ conn,
+ "/oauth/twitter/callback",
+ %{
+ "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
+ "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
+ "provider" => "twitter",
+ "state" => Poison.encode!(state_params)
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/
+ end
+ end
+
+ test "with user-unbound registration, GET /oauth/<provider>/callback redirects to registration_details page",
+ %{app: app, conn: conn} do
+ registration = insert(:registration, user: nil)
+
+ state_params = %{
+ "scope" => "read",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state"
+ }
+
+ with_mock Pleroma.Web.Auth.Authenticator,
+ get_registration: fn _, _ -> {:ok, registration} end do
+ conn =
+ get(
+ conn,
+ "/oauth/twitter/callback",
+ %{
+ "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
+ "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
+ "provider" => "twitter",
+ "state" => Poison.encode!(state_params)
+ }
+ )
+
+ expected_redirect_params =
+ state_params
+ |> Map.delete("scope")
+ |> Map.merge(%{
+ "scopes" => ["read"],
+ "email" => Registration.email(registration),
+ "nickname" => Registration.nickname(registration)
+ })
+
+ assert response = html_response(conn, 302)
+
+ assert redirected_to(conn) ==
+ o_auth_path(conn, :registration_details, expected_redirect_params)
+ end
+ end
+
+ test "GET /oauth/registration_details renders registration details form", %{
+ app: app,
+ conn: conn
+ } do
+ conn =
+ get(
+ conn,
+ "/oauth/registration_details",
+ %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state",
+ "nickname" => nil,
+ "email" => "john@doe.com"
+ }
+ )
+
+ assert response = html_response(conn, 200)
+ assert response =~ ~r/name="op" type="submit" value="register"/
+ assert response =~ ~r/name="op" type="submit" value="connect"/
+ end
+
+ test "with valid params, POST /oauth/register?op=register redirects to `redirect_uri` with `code`",
+ %{
+ app: app,
+ conn: conn
+ } do
+ registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post(
+ "/oauth/register",
+ %{
+ "op" => "register",
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state",
+ "nickname" => "availablenick",
+ "email" => "available@email.com"
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/
+ end
+
+ test "with invalid params, POST /oauth/register?op=register redirects to registration_details page",
+ %{
+ app: app,
+ conn: conn
+ } do
+ another_user = insert(:user)
+ registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
+
+ params = %{
+ "op" => "register",
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state",
+ "nickname" => another_user.nickname,
+ "email" => another_user.email
+ }
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post("/oauth/register", params)
+
+ assert response = html_response(conn, 302)
+
+ assert redirected_to(conn) ==
+ o_auth_path(conn, :registration_details, params)
+ end
+
+ test "with valid params, POST /oauth/register?op=connect redirects to `redirect_uri` with `code`",
+ %{
+ app: app,
+ conn: conn
+ } do
+ user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("testpassword"))
+ registration = insert(:registration, user: nil)
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post(
+ "/oauth/register",
+ %{
+ "op" => "connect",
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state",
+ "auth_name" => user.nickname,
+ "password" => "testpassword"
+ }
+ )
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/
+ end
+
+ test "with invalid params, POST /oauth/register?op=connect redirects to registration_details page",
+ %{
+ app: app,
+ conn: conn
+ } do
+ user = insert(:user)
+ registration = insert(:registration, user: nil)
+
+ params = %{
+ "op" => "connect",
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state",
+ "auth_name" => user.nickname,
+ "password" => "wrong password"
+ }
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post("/oauth/register", params)
+
+ assert response = html_response(conn, 302)
+
+ assert redirected_to(conn) ==
+ o_auth_path(conn, :registration_details, Map.delete(params, "password"))
+ end
+ end
+
+ describe "GET /oauth/authorize" do
+ setup do
[
app: insert(:oauth_app, redirect_uris: "https://redirect.url"),
conn:
build_conn()
- |> Plug.Session.call(Plug.Session.init(session_opts))
+ |> Plug.Session.call(Plug.Session.init(@session_opts))
|> fetch_session()
]
end