mobilizon/test/web/controllers/application_controller_test.exs
Thomas Citharel b315e1d7ff
refactor: use Phoenix verified routes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 11:29:53 +01:00

634 lines
20 KiB
Elixir

defmodule Mobilizon.Web.ApplicationControllerTest do
use Mobilizon.Web.ConnCase
alias Mobilizon.Service.Auth.Applications
alias Mobilizon.Web.Router.Helpers, as: Routes
import Mobilizon.Factory
describe "create application" do
test "requires all parameters",
%{conn: conn} do
conn =
conn
|> post("/apps", %{"name" => "hello"})
assert error = json_response(conn, 400)
assert error["error"] == "invalid_request"
assert error["error_description"] ==
"All of name, scope and redirect_uri parameters are required to create an application"
end
test "requires valid scopes",
%{conn: conn} do
conn =
conn
|> post("/apps", %{
"name" => "hello",
"redirect_uri" => "hello",
"scope" => "write nothing"
})
assert error = json_response(conn, 400)
assert error["error"] == "invalid_scope"
assert error["error_description"] ==
"The scope parameter is not a space separated list of valid scopes"
end
test "works",
%{conn: conn} do
name = "hello"
redirect_uris = ["hello", "world"]
scope = "read write:event:create"
website = "hi"
conn =
conn
|> post("/apps", %{
"name" => name,
"redirect_uri" => Enum.join(redirect_uris, "\n"),
"scope" => scope,
"website" => website
})
assert %{
"name" => ^name,
"redirect_uri" => ^redirect_uris,
"scope" => ^scope,
"website" => ^website,
"client_id" => _client_id,
"client_secret" => _client_secret
} = json_response(conn, 200)
end
end
describe "authorize" do
test "without a valid URI", %{conn: conn} do
conn = get(conn, "/oauth/authorize?client_id=hello&redirect_uri=toto")
assert response(conn, 400) ==
"You need to provide a valid redirect_uri to autorize an application"
end
test "without all valid params", %{conn: conn} do
conn =
get(
conn,
"/oauth/authorize?client_id=hello&redirect_uri=#{URI.encode("https://somewhere.org/callback")}"
)
assert redirected_to(conn) =~
"error=invalid_request&error_description=#{URI.encode_www_form("You need to specify client_id, redirect_uri, scope and state to autorize an application")}"
end
test "with all required params redirects to authorization page", %{conn: conn} do
conn =
get(
conn,
"/oauth/authorize?client_id=hello&redirect_uri=#{URI.encode("https://somewhere.org/callback&state=something&scope=everything")}"
)
assert redirected_to(conn) =~ "/oauth/autorize_approve"
end
end
describe "generate device code" do
test "without all required params", %{conn: conn} do
conn = post(conn, "/login/device/code", client_id: "hello")
assert error = json_response(conn, 400)
assert error["error"] == "invalid_request"
assert error["error_description"] ==
"You need to pass both client_id and scope as parameters to obtain a device code"
end
test "with an invalid client_id", %{conn: conn} do
conn = post(conn, "/login/device/code", client_id: "hello", scope: "write:event:create")
assert error = json_response(conn, 400)
assert error["error"] == "invalid_client"
assert error["error_description"] == "No application was found with this client_id"
end
test "with a scope not matching app registered scopes", %{conn: conn} do
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
conn =
post(conn, "/login/device/code", client_id: app.client_id, scope: "write:event:delete")
assert error = json_response(conn, 400)
assert error["error"] == "invalid_scope"
assert error["error_description"] ==
"The given scope is not in the list of the app declared scopes"
end
test "with valid params gives a URL-encoded code", %{conn: conn} do
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
conn =
post(conn, "/login/device/code", client_id: app.client_id, scope: "write:event:create")
res = json_response(conn, 200)
verification_uri = url(~p"/login/device")
assert %{
"device_code" => _device_code,
"expires_in" => 900,
"interval" => 5,
"user_code" => user_code,
"verification_uri" => ^verification_uri
} = res
assert Regex.match?(~r/^[A-Z]{4}-[A-Z]{4}$/, user_code)
end
test "with valid params and a JSON Accept header gives a JSON-encoded struct", %{conn: conn} do
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
conn =
conn
|> Plug.Conn.put_req_header("accept", "application/json")
|> post("/login/device/code", client_id: app.client_id, scope: "write:event:create")
res = json_response(conn, 200)
verification_uri = url(~p"/login/device")
assert %{
"device_code" => _device_code,
"expires_in" => 900,
"interval" => 5,
"user_code" => user_code,
"verification_uri" => ^verification_uri
} = res
assert Regex.match?(~r/^[A-Z]{4}-[A-Z]{4}$/, user_code)
end
end
describe "generate access code for device flow" do
test "without valid parameters", %{conn: conn} do
conn = post(conn, "/oauth/token")
assert error = json_response(conn, 400)
assert error["error"] == "invalid_request"
assert error["error_description"] ==
"Incorrect parameters sent. You need to provide at least the grant_type and client_id parameters, depending on the grant type being used."
end
test "with invalid client_id", %{conn: conn} do
conn =
post(conn, "/oauth/token",
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
client_id: "some_client_id",
device_code: "hello"
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_grant"
assert error["error_description"] ==
"The client_id provided or the device_code associated is invalid"
end
test "with rejected authorization", %{conn: conn} do
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
assert {:ok, _res} =
Mobilizon.Applications.create_application_device_activation(%{
device_code: "hello",
user_code: "world",
expires_in: 900,
application_id: app.id,
scope: "write:event:create write:event:update",
status: :access_denied
})
conn =
post(conn, "/oauth/token",
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
client_id: app.client_id,
device_code: "hello"
)
assert error = json_response(conn, 400)
assert error["error"] == "access_denied"
assert error["error_description"] == "The user rejected the requested authorization"
end
test "with incorrect device code", %{conn: conn} do
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
assert {:ok, _res} =
Mobilizon.Applications.create_application_device_activation(%{
device_code: "hello",
user_code: "world",
expires_in: 900,
application_id: app.id,
scope: "write:event:create write:event:update",
status: "incorrect_device_code"
})
conn =
post(conn, "/oauth/token",
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
client_id: app.client_id,
device_code: "hello"
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_grant"
assert error["error_description"] ==
"The client_id provided or the device_code associated is invalid"
end
test "with an expired device activation", %{conn: conn} do
user = insert(:user)
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
assert {:ok, _res} =
Mobilizon.Applications.create_application_device_activation(%{
device_code: "hello",
user_code: "world",
expires_in: -40,
application_id: app.id,
scope: "write:event:create write:event:update",
status: "success",
user_id: user.id
})
conn =
post(conn, "/oauth/token",
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
client_id: app.client_id,
device_code: "hello"
)
assert error = json_response(conn, 400)
assert error["error"] == "expired_token"
assert error["error_description"] ==
"The given device_code has expired"
end
test "with a pending authorization", %{conn: conn} do
user = insert(:user)
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
assert {:ok, _res} =
Mobilizon.Applications.create_application_device_activation(%{
device_code: "hello",
user_code: "world",
expires_in: 600,
application_id: app.id,
scope: "write:event:create write:event:update",
status: "pending",
user_id: user.id
})
conn =
conn
|> Plug.Conn.put_req_header("accept", "application/json")
|> post("/oauth/token",
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
client_id: app.client_id,
device_code: "hello"
)
error = json_response(conn, 400)
assert error["error"] == "authorization_pending"
assert error["error_description"] == "The authorization request is still pending"
conn =
Phoenix.ConnTest.build_conn()
|> Plug.Conn.put_req_header("accept", "application/json")
|> post("/oauth/token",
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
client_id: app.client_id,
device_code: "hello"
)
error = json_response(conn, 400)
assert error["error"] == "slow_down"
assert error["error_description"] == "Please slow down the rate of your requests"
end
test "with valid params as JSON", %{conn: conn} do
user = insert(:user)
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
assert {:ok, _res} =
Mobilizon.Applications.create_application_device_activation(%{
device_code: "hello",
user_code: "world",
expires_in: 600,
application_id: app.id,
scope: "write:event:create write:event:update",
status: "success",
user_id: user.id
})
conn =
conn
|> Plug.Conn.put_req_header("accept", "application/json")
|> post("/oauth/token",
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
client_id: app.client_id,
device_code: "hello"
)
res = json_response(conn, 200)
assert %{
"access_token" => _access_token,
"expires_in" => 28_800,
"refresh_token" => _refresh_token,
"refresh_token_expires_in" => 15_724_800,
"scope" => "write:event:create write:event:update",
"token_type" => "bearer"
} = res
end
end
describe "generate access code for authorization flow" do
test "with invalid client_id", %{conn: conn} do
conn =
post(conn, "/oauth/token",
grant_type: "authorization_code",
client_id: "some_client_id",
client_secret: "some_client_secret",
code: "hello",
redirect_uri: "some redirect uri",
scope: "hello"
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_request"
assert json_response(conn, 400)["error_description"] ==
"No application was found with this client_id"
end
test "with invalid redirect_uri", %{conn: conn} do
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
conn =
post(conn, "/oauth/token",
grant_type: "authorization_code",
client_id: app.client_id,
client_secret: app.client_secret,
code: "hello",
redirect_uri: "nope",
scope: "write:event:create"
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_request"
assert error["error_description"] ==
"This redirect URI is not allowed"
end
test "with invalid code", %{conn: conn} do
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
conn =
post(conn, "/oauth/token",
grant_type: "authorization_code",
client_id: app.client_id,
client_secret: app.client_secret,
code: "hello",
redirect_uri: "hello",
scope: "write:event:create"
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_grant"
assert error["error_description"] ==
"The provided code is invalid or expired"
end
test "with invalid client secret", %{conn: conn} do
user = insert(:user)
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
Mobilizon.Applications.create_application_token(%{
user_id: user.id,
application_id: app.id,
authorization_code: "hi there",
scope: "write:event:create write:event:update"
})
conn =
post(conn, "/oauth/token",
grant_type: "authorization_code",
client_id: app.client_id,
client_secret: "not the client secret",
code: "hi there",
redirect_uri: "hello",
scope: "write:event:create write:event:update"
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_client"
assert error["error_description"] ==
"The provided client_secret is invalid"
end
test "with an authorization code matching a different app", %{conn: conn} do
user = insert(:user)
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
{:ok, app2} =
Applications.create("My other app", ["hello"], "write:event:create write:event:update")
Mobilizon.Applications.create_application_token(%{
user_id: user.id,
application_id: app2.id,
authorization_code: "hi there",
scope: "write:event:create write:event:update"
})
conn =
post(conn, "/oauth/token",
grant_type: "authorization_code",
client_id: app.client_id,
client_secret: app.client_id,
code: "hi there",
redirect_uri: "hello",
scope: "write:event:create write:event:update"
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_grant"
assert error["error_description"] ==
"The provided client_id does not match the provided code"
end
test "with valid params", %{conn: conn} do
user = insert(:user)
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
Mobilizon.Applications.create_application_token(%{
user_id: user.id,
application_id: app.id,
authorization_code: "hi there",
scope: "write:event:create write:event:update"
})
conn =
post(conn, "/oauth/token",
grant_type: "authorization_code",
client_id: app.client_id,
client_secret: app.client_secret,
code: "hi there",
redirect_uri: "hello",
scope: "write:event:create write:event:update"
)
res = json_response(conn, 200)
assert %{
"access_token" => _access_token,
"expires_in" => 28_800,
"refresh_token" => _refresh_token,
"refresh_token_expires_in" => 15_724_800,
"scope" => "write:event:create write:event:update",
"token_type" => "bearer"
} = res
end
end
describe "generate new access code from refresh code" do
test "with invalid refresh token", %{conn: conn} do
conn =
post(conn, "/oauth/token",
grant_type: "refresh_token",
client_id: "hello",
client_secret: "secret",
refresh_token: "none"
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_grant"
assert error["error_description"] ==
"Invalid refresh token provided"
end
test "with invalid client credentials", %{conn: conn} do
user = insert(:user)
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
Mobilizon.Applications.create_application_token(%{
user_id: user.id,
application_id: app.id,
authorization_code: "hi there",
scope: "write:event:create write:event:update"
})
conn =
post(conn, "/oauth/token",
grant_type: "authorization_code",
client_id: app.client_id,
client_secret: app.client_secret,
code: "hi there",
redirect_uri: "hello",
scope: "write:event:create write:event:update"
)
res = json_response(conn, 200)
conn =
post(conn, "/oauth/token",
grant_type: "refresh_token",
client_id: "hello",
client_secret: "secret",
refresh_token: res["refresh_token"]
)
assert error = json_response(conn, 400)
assert error["error"] == "invalid_client"
assert error["error_description"] ==
"Invalid client credentials provided"
end
test "with valid params", %{conn: conn} do
user = insert(:user)
{:ok, app} =
Applications.create("My app", ["hello"], "write:event:create write:event:update")
Mobilizon.Applications.create_application_token(%{
user_id: user.id,
application_id: app.id,
authorization_code: "hi there",
scope: "write:event:create write:event:update"
})
conn =
post(conn, "/oauth/token",
grant_type: "authorization_code",
client_id: app.client_id,
client_secret: app.client_secret,
code: "hi there",
redirect_uri: "hello",
scope: "write:event:create write:event:update"
)
res = json_response(conn, 200)
conn =
post(conn, "/oauth/token",
grant_type: "refresh_token",
client_id: app.client_id,
client_secret: app.client_secret,
refresh_token: res["refresh_token"]
)
res = json_response(conn, 200)
assert %{
"access_token" => _access_token,
"expires_in" => 28_800,
"refresh_token" => _refresh_token,
"refresh_token_expires_in" => 15_724_800,
"scope" => "write:event:create write:event:update",
"token_type" => "bearer"
} = res
end
end
end