mobilizon.chapril.org-mobil.../test/graphql/resolvers/user_test.exs
Thomas Citharel 783486a366
Support denying registration based on email or domain
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-08-08 19:56:33 +02:00

1474 lines
43 KiB
Elixir

defmodule Mobilizon.GraphQL.Resolvers.UserTest do
use Mobilizon.Web.ConnCase
use Bamboo.Test
use Oban.Testing, repo: Mobilizon.Storage.Repo
import Mobilizon.Factory
alias Mobilizon.{Actors, Config, Discussions, Events, Users}
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Service.Auth.Authenticator
alias Mobilizon.Users.User
alias Mobilizon.GraphQL.AbsintheHelpers
alias Mobilizon.Web.Email
@get_user_query """
query GetUser($id: ID!) {
user(id: $id) {
id
email
}
}
"""
@logged_user_query """
query LoggedUser {
loggedUser {
id
email
}
}
"""
@list_users_query """
query ListUsers($page: Int, $limit: Int, $sort: SortableUserField, $direction: SortDirection) {
users(page: $page, limit: $limit, sort: $sort, direction: $direction) {
total,
elements {
email
}
}
}
"""
@create_user_mutation """
mutation CreateUser($email: String!, $password: String!, $locale: String) {
createUser(
email: $email
password: $password
locale: $locale
) {
id,
email,
locale
}
}
"""
@register_person_mutation """
mutation RegisterPerson($preferredUsername: String!, $name: String, $summary: String, $email: String!) {
registerPerson(
preferredUsername: $preferredUsername,
name: $name,
summary: $summary,
email: $email,
) {
preferredUsername,
name,
summary,
avatar {
url
},
}
}
"""
@change_email_mutation """
mutation ChangeEmail($email: String!, $password: String!) {
changeEmail(email: $email, password: $password) {
id
}
}
"""
@login_mutation """
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
accessToken,
refreshToken,
user {
id
}
}
}
"""
@validate_email_mutation """
mutation ValidateEmail($token: String!) {
validateEmail(
token: $token
) {
id
}
}
"""
@send_reset_password_mutation """
mutation SendResetPassword($email: String!) {
sendResetPassword(email: $email)
}
"""
@delete_user_account_mutation """
mutation DeleteAccount($password: String) {
deleteAccount (password: $password) {
id
}
}
"""
@change_default_actor """
mutation ChangeDefaultActor($preferredUsername: String!) {
changeDefaultActor(preferredUsername: $preferredUsername) {
defaultActor {
preferredUsername
}
}
}
"""
@valid_actor_params %{email: "test@test.tld", password: "testest", username: "test"}
@valid_single_actor_params %{preferred_username: "test2", keys: "yolo"}
describe "Resolver: Get an user" do
test "find_user/3 returns an user by its id", %{conn: conn} do
user = insert(:user)
modo = insert(:user, role: :moderator)
res =
conn
|> auth_conn(modo)
|> AbsintheHelpers.graphql_query(
query: @get_user_query,
variables: %{id: user.id}
)
assert res["data"]["user"]["email"] == user.email
res =
conn
|> auth_conn(modo)
|> AbsintheHelpers.graphql_query(
query: @get_user_query,
variables: %{id: 0}
)
assert res["data"]["user"] == nil
assert hd(res["errors"])["message"] == "User with ID #{0} not found"
end
test "get_current_user/3 returns the current logged-in user", %{conn: conn} do
user = insert(:user)
res =
conn
|> AbsintheHelpers.graphql_query(
query: @logged_user_query,
variables: %{}
)
assert res["data"]["loggedUser"] == nil
assert hd(res["errors"])["message"] ==
"You need to be logged in"
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @logged_user_query,
variables: %{}
)
assert res["data"]["loggedUser"]["id"] == to_string(user.id)
end
end
describe "Resolver: List users" do
test "list_users/3 doesn't return anything with a non moderator user", %{conn: conn} do
insert(:user, email: "riri@example.com", role: :moderator)
user = insert(:user, email: "fifi@example.com")
insert(:user, email: "loulou@example.com", role: :administrator)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @list_users_query,
variables: %{}
)
assert hd(res["errors"])["message"] ==
"You don't have permission to do this"
end
test "list_users/3 returns a list of users", %{conn: conn} do
user = insert(:user, email: "riri@example.com", role: :moderator)
insert(:user, email: "fifi@example.com")
insert(:user, email: "loulou@example.com")
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @list_users_query,
variables: %{}
)
assert res["errors"] == nil
assert res["data"]["users"]["total"] == 3
assert res["data"]["users"]["elements"] |> length == 3
assert res["data"]["users"]["elements"]
|> Enum.map(& &1["email"]) == [
"loulou@example.com",
"fifi@example.com",
"riri@example.com"
]
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @list_users_query,
variables: %{page: 2, limit: 1}
)
assert res["errors"] == nil
assert res["data"]["users"]["total"] == 3
assert res["data"]["users"]["elements"] |> length == 1
assert res["data"]["users"]["elements"] |> Enum.map(& &1["email"]) == [
"fifi@example.com"
]
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @list_users_query,
variables: %{page: 3, limit: 1, sort: "ID", direction: "DESC"}
)
assert res["errors"] == nil
assert res["data"]["users"]["total"] == 3
assert res["data"]["users"]["elements"] |> length == 1
assert res["data"]["users"]["elements"] |> Enum.map(& &1["email"]) == [
"riri@example.com"
]
end
end
describe "Resolver: Create an user & actor" do
@user_creation %{
email: "test@demo.tld",
password: "long password",
locale: "fr_FR",
preferredUsername: "toto",
name: "Sir Toto",
summary: "Sir Toto, prince of the functional tests"
}
@user_creation_bad_email %{
email: "y@l@",
password: "long password"
}
test "test create_user/3 creates an user and register_person/3 registers a profile",
%{conn: conn} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
assert res["data"]["createUser"]["email"] == @user_creation.email
assert res["data"]["createUser"]["locale"] == @user_creation.locale
{:ok, user} = Users.get_user_by_email(@user_creation.email)
assert_delivered_email(Email.User.confirmation_email(user, @user_creation.locale))
res =
conn
|> AbsintheHelpers.graphql_query(
query: @register_person_mutation,
variables: @user_creation
)
assert res["data"]["registerPerson"]["preferredUsername"] ==
@user_creation.preferredUsername
end
test "create_user/3 doesn't allow two users with the same email", %{conn: conn} do
res =
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
assert res["data"]["createUser"]["email"] == @user_creation.email
res =
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
assert hd(res["errors"])["message"] == ["Cette adresse e-mail est déjà utilisée."]
end
test "create_user/3 doesn't allow registration when registration is closed", %{conn: conn} do
Config.put([:instance, :registrations_open], false)
Config.put([:instance, :registration_email_allowlist], [])
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
assert hd(res["errors"])["message"] == "Registrations are not open"
Config.put([:instance, :registrations_open], true)
end
test "create_user/3 doesn't allow registration when user email is not on the allowlist", %{
conn: conn
} do
Config.put([:instance, :registrations_open], false)
Config.put([:instance, :registration_email_allowlist], ["random.org"])
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
assert hd(res["errors"])["message"] == "Your email is not on the allowlist"
Config.put([:instance, :registrations_open], true)
Config.put([:instance, :registration_email_allowlist], [])
end
test "create_user/3 allows registration when user email domain is on the allowlist", %{
conn: conn
} do
Config.put([:instance, :registrations_open], false)
Config.put([:instance, :registration_email_allowlist], ["demo.tld"])
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
refute res["errors"]
assert res["data"]["createUser"]["email"] == @user_creation.email
Config.put([:instance, :registrations_open], true)
Config.put([:instance, :registration_email_allowlist], [])
end
test "create_user/3 allows registration when user email is on the allowlist", %{conn: conn} do
Config.put([:instance, :registrations_open], false)
Config.put([:instance, :registration_email_allowlist], [@user_creation.email])
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
refute res["errors"]
assert res["data"]["createUser"]["email"] == @user_creation.email
Config.put([:instance, :registrations_open], true)
Config.put([:instance, :registration_email_allowlist], [])
end
test "create_user/3 doesn't allow registration when user email domain is on the denylist", %{
conn: conn
} do
Config.put([:instance, :registration_email_denylist], ["demo.tld"])
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
assert hd(res["errors"])["message"] ==
"Your e-mail has been denied registration or uses a disallowed e-mail provider"
Config.put([:instance, :registrations_open], true)
Config.put([:instance, :registration_email_denylist], [])
end
test "create_user/3 doesn't allow registration when user email is on the denylist", %{
conn: conn
} do
Config.put([:instance, :registration_email_denylist], [@user_creation.email])
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
assert hd(res["errors"])["message"] ==
"Your e-mail has been denied registration or uses a disallowed e-mail provider"
Config.put([:instance, :registrations_open], true)
Config.put([:instance, :registration_email_denylist], [])
end
test "create_user/3 doesn't allow registration when user email is on the denylist, even when plus addressing is used",
%{
conn: conn
} do
Config.put([:instance, :registration_email_denylist], [@user_creation.email])
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: Map.put(@user_creation, :email, "test+alias@demo.tld")
)
assert hd(res["errors"])["message"] ==
"Your e-mail has been denied registration or uses a disallowed e-mail provider"
Config.put([:instance, :registrations_open], true)
Config.put([:instance, :registration_email_denylist], [])
end
test "register_person/3 doesn't register a profile from an unknown email", %{conn: conn} do
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
res =
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @register_person_mutation,
variables: Map.put(@user_creation, :email, "random")
)
assert hd(res["errors"])["message"] ==
"Aucun·e utilisateur·ice avec cette adresse e-mail n'a été trouvé·e"
end
test "register_person/3 can't be called with an existing profile", %{conn: conn} do
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
res =
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @register_person_mutation,
variables: @user_creation
)
assert res["data"]["registerPerson"]["preferredUsername"] ==
@user_creation.preferredUsername
res =
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @register_person_mutation,
variables: @user_creation
)
assert hd(res["errors"])["message"] ==
"Vous avez déjà un profil pour cet utilisateur"
end
test "register_person/3 is case insensitive", %{conn: conn} do
insert(:actor, preferred_username: "myactor")
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation
)
res =
conn
|> put_req_header("accept-language", "fr")
|> AbsintheHelpers.graphql_query(
query: @register_person_mutation,
variables: Map.put(@user_creation, :preferredUsername, "Myactor")
)
refute is_nil(res["errors"])
assert hd(res["errors"])["message"] ==
["Cet identifiant est déjà pris."]
end
test "test create_user/3 doesn't create an user with bad email", %{conn: conn} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @create_user_mutation,
variables: @user_creation_bad_email
)
assert hd(res["errors"])["message"] ==
["Email doesn't fit required format"]
end
end
describe "Resolver: Validate an user" do
test "test validate_user/3 validates an user", context do
{:ok, %User{} = user} = Users.register(@valid_actor_params)
mutation = """
mutation {
validateUser(
token: "#{user.confirmation_token}"
) {
accessToken,
user {
id,
},
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert json_response(res, 200)["data"]["validateUser"]["user"]["id"] == to_string(user.id)
end
test "test validate_user/3 with invalid token doesn't validate an user", context do
insert(:user, confirmation_token: "t0t0")
mutation = """
mutation {
validateUser(
token: "no pass"
) {
accessToken,
user {
id
},
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] == "Unable to validate user"
end
end
describe "Resolver: Resend confirmation emails" do
test "test resend_confirmation_email/3 with valid email resends an validation email",
context do
{:ok, %User{} = user} = Users.register(%{email: "toto@tata.tld", password: "p4ssw0rd"})
mutation = """
mutation {
resendConfirmationEmail(
email: "#{user.email}"
)
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"You requested again a confirmation email too soon"
# Hammer time !
Users.update_user(user, %{
confirmation_sent_at: Timex.shift(user.confirmation_sent_at, hours: -3)
})
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert json_response(res, 200)["data"]["resendConfirmationEmail"] == user.email
assert_delivered_email(Email.User.confirmation_email(user))
end
test "test resend_confirmation_email/3 with invalid email resends an validation email",
context do
mutation = """
mutation {
resendConfirmationEmail(
email: "oh no"
)
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"No user to validate with this email was found"
end
end
describe "Resolver: Send reset password" do
test "test send_reset_password/3 with valid email", %{conn: conn} do
%User{email: email} = insert(:user)
res =
conn
|> AbsintheHelpers.graphql_query(
query: @send_reset_password_mutation,
variables: %{email: email}
)
assert res["data"]["sendResetPassword"] == email
end
test "test send_reset_password/3 with invalid email", %{conn: conn} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @send_reset_password_mutation,
variables: %{email: "not an email"}
)
assert hd(res["errors"])["message"] ==
"No user with this email was found"
end
test "test send_reset_password/3 for an LDAP user", %{conn: conn} do
{:ok, %User{email: email}} = Users.create_external("some@users.com", "ldap")
res =
conn
|> AbsintheHelpers.graphql_query(
query: @send_reset_password_mutation,
variables: %{email: email}
)
assert hd(res["errors"])["message"] ==
"This user can't reset their password"
end
test "test send_reset_password/3 for a deactivated user doesn't send email", %{conn: conn} do
{:ok, %User{email: email} = user} =
Users.register(%{email: "toto@tata.tld", password: "p4ssw0rd"})
Users.update_user(user, %{confirmed_at: DateTime.utc_now(), disabled: true})
res =
conn
|> AbsintheHelpers.graphql_query(
query: @send_reset_password_mutation,
variables: %{email: email}
)
assert hd(res["errors"])["message"] ==
"No user with this email was found"
end
end
describe "Resolver: Reset user's password" do
test "test reset_password/3 with valid email", context do
{:ok, %User{} = user} = Users.register(%{email: "toto@tata.tld", password: "p4ssw0rd"})
Users.update_user(user, %{confirmed_at: DateTime.utc_now()})
%Actor{} = insert(:actor, user: user)
{:ok, _email_sent} = Email.User.send_password_reset_email(user)
%User{reset_password_token: reset_password_token} = Users.get_user!(user.id)
mutation = """
mutation {
resetPassword(
token: "#{reset_password_token}",
password: "new password"
) {
user {
id
}
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert is_nil(json_response(res, 200)["errors"])
assert json_response(res, 200)["data"]["resetPassword"]["user"]["id"] == to_string(user.id)
end
test "test reset_password/3 with a password too short", context do
%User{} = user = insert(:user)
{:ok, _email_sent} = Email.User.send_password_reset_email(user)
%User{reset_password_token: reset_password_token} = Users.get_user!(user.id)
mutation = """
mutation {
resetPassword(
token: "#{reset_password_token}",
password: "new"
) {
user {
id
}
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"The password you have choosen is too short. Please make sure your password contains at least 6 charaters."
end
test "test reset_password/3 with an invalid token", context do
%User{} = user = insert(:user)
{:ok, _email_sent} = Email.User.send_password_reset_email(user)
%User{} = Users.get_user!(user.id)
mutation = """
mutation {
resetPassword(
token: "not good",
password: "new"
) {
user {
id
}
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"The token you provided is invalid. Make sure that the URL is exactly the one provided inside the email you got."
end
end
describe "Resolver: Login a user" do
test "test login_user/3 with valid credentials", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: "toto@tata.tld", password: "p4ssw0rd"})
{:ok, %User{} = _user} =
Users.update_user(user, %{
"confirmed_at" => DateTime.utc_now() |> DateTime.truncate(:second),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
})
res =
conn
|> AbsintheHelpers.graphql_query(
query: @login_mutation,
variables: %{email: user.email, password: user.password}
)
assert login = res["data"]["login"]
assert Map.has_key?(login, "accessToken") && not is_nil(login["accessToken"])
end
test "test login_user/3 with invalid password", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: "toto@tata.tld", password: "p4ssw0rd"})
{:ok, %User{} = _user} =
Users.update_user(user, %{
"confirmed_at" => DateTime.utc_now() |> DateTime.truncate(:second),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
})
res =
conn
|> AbsintheHelpers.graphql_query(
query: @login_mutation,
variables: %{email: user.email, password: "bad password"}
)
assert hd(res["errors"])["message"] ==
"Impossible to authenticate, either your email or password are invalid."
end
test "test login_user/3 with invalid email", %{conn: conn} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @login_mutation,
variables: %{email: "bad email", password: "bad password"}
)
assert hd(res["errors"])["message"] ==
"User not found"
end
test "test login_user/3 with unconfirmed user", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: "toto@tata.tld", password: "p4ssw0rd"})
res =
conn
|> AbsintheHelpers.graphql_query(
query: @login_mutation,
variables: %{email: user.email, password: user.password}
)
assert hd(res["errors"])["message"] == "User not found"
end
end
describe "Resolver: Refresh a token" do
test "test refresh_token/3 with a bad token", context do
mutation = """
mutation {
refreshToken(
refreshToken: "bad_token"
) {
accessToken
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"Cannot refresh the token"
end
test "test refresh_token/3 with an appropriate token", context do
user = insert(:user)
{:ok, refresh_token} = Authenticator.generate_refresh_token(user)
mutation = """
mutation {
refreshToken(
refreshToken: "#{refresh_token}"
) {
accessToken
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert json_response(res, 200)["errors"] == nil
access_token = json_response(res, 200)["data"]["refreshToken"]["accessToken"]
assert String.length(access_token) > 10
query = """
{
loggedPerson {
preferredUsername,
}
}
"""
res =
context.conn
|> Plug.Conn.put_req_header("authorization", "Bearer #{access_token}")
|> post("/api", AbsintheHelpers.query_skeleton(query, "logged_person"))
assert json_response(res, 200)["errors"] == nil
end
end
describe "Resolver: change default actor for user" do
test "test change_default_actor/3 without being logged-in", %{conn: conn} do
# Prepare user with two actors
user = insert(:user)
insert(:actor, user: user)
assert {:ok, %User{actors: _actors}} = Users.get_user_with_actors(user.id)
actor_params = @valid_single_actor_params |> Map.put(:user_id, user.id)
assert {:ok, %Actor{} = actor2} = Actors.create_actor(actor_params)
assert {:ok, %User{actors: actors}} = Users.get_user_with_actors(user.id)
assert length(actors) == 2
res =
conn
|> AbsintheHelpers.graphql_query(
query: @change_default_actor,
variables: %{preferredUsername: actor2.preferred_username}
)
assert hd(res["errors"])["message"] == "You need to be logged in"
end
test "test change_default_actor/3 with valid actor", %{conn: conn} do
# Prepare user with two actors
user = insert(:user)
insert(:actor, user: user)
assert {:ok, %User{actors: _actors}} = Users.get_user_with_actors(user.id)
actor_params = @valid_single_actor_params |> Map.put(:user_id, user.id)
assert {:ok, %Actor{} = actor2} = Actors.create_actor(actor_params)
assert {:ok, %User{actors: actors}} = Users.get_user_with_actors(user.id)
assert length(actors) == 2
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @change_default_actor,
variables: %{preferredUsername: actor2.preferred_username}
)
assert res["errors"] == nil
assert res["data"]["changeDefaultActor"]["defaultActor"]["preferredUsername"] ==
actor2.preferred_username
end
end
describe "Resolver: Change password for an user" do
@email "toto@tata.tld"
@old_password "p4ssw0rd"
@new_password "upd4t3d"
test "change_password/3 with valid password", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @email, password: @old_password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
"confirmed_at" => Timex.shift(user.confirmation_sent_at, hours: -3),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
})
mutation = """
mutation {
login(
email: "#{@email}",
password: "#{@old_password}",
) {
accessToken,
refreshToken,
user {
id
}
}
}
"""
res =
conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert login = json_response(res, 200)["data"]["login"]
assert Map.has_key?(login, "accessToken") && not is_nil(login["accessToken"])
mutation = """
mutation {
changePassword(old_password: "#{@old_password}", new_password: "#{@new_password}") {
id
}
}
"""
res =
conn
|> auth_conn(user)
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert json_response(res, 200)["errors"] == nil
assert json_response(res, 200)["data"]["changePassword"]["id"] == to_string(user.id)
mutation = """
mutation {
login(
email: "#{@email}",
password: "#{@new_password}",
) {
accessToken,
refreshToken,
user {
id
}
}
}
"""
res =
conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert login = json_response(res, 200)["data"]["login"]
assert Map.has_key?(login, "accessToken") && not is_nil(login["accessToken"])
end
test "change_password/3 with invalid password", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @email, password: @old_password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
"confirmed_at" => Timex.shift(user.confirmation_sent_at, hours: -3),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
})
mutation = """
mutation {
changePassword(old_password: "invalid password", new_password: "#{@new_password}") {
id
}
}
"""
res =
conn
|> auth_conn(user)
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] == "The current password is invalid"
end
test "change_password/3 with same password", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @email, password: @old_password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
"confirmed_at" => Timex.shift(user.confirmation_sent_at, hours: -3),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
})
mutation = """
mutation {
changePassword(old_password: "#{@old_password}", new_password: "#{@old_password}") {
id
}
}
"""
res =
conn
|> auth_conn(user)
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"The new password must be different"
end
test "change_password/3 with new password too short", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @email, password: @old_password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
"confirmed_at" => Timex.shift(user.confirmation_sent_at, hours: -3),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
})
mutation = """
mutation {
changePassword(old_password: "#{@old_password}", new_password: "new") {
id
}
}
"""
res =
conn
|> auth_conn(user)
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"The password you have chosen is too short. Please make sure your password contains at least 6 characters."
end
test "change_password/3 without being authenticated", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @email, password: @old_password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
"confirmed_at" => Timex.shift(user.confirmation_sent_at, hours: -3),
"confirmation_sent_at" => nil,
"confirmation_token" => nil
})
mutation = """
mutation {
changePassword(old_password: "#{@old_password}", new_password: "#{@new_password}") {
id
}
}
"""
res =
conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"You need to be logged-in to change your password"
end
end
describe "Resolver: Change email for an user" do
@old_email "old@domain.tld"
@new_email "new@domain.tld"
@password "p4ssw0rd"
test "change_email/3 with valid email", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @old_email, password: @password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
res =
conn
|> AbsintheHelpers.graphql_query(
query: @login_mutation,
variables: %{email: @old_email, password: @password}
)
login = res["data"]["login"]
assert Map.has_key?(login, "accessToken") && not is_nil(login["accessToken"])
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @change_email_mutation,
variables: %{email: @new_email, password: @password}
)
assert res["errors"] == nil
assert res["data"]["changeEmail"]["id"] == to_string(user.id)
user = Users.get_user!(user.id)
assert user.email == @old_email
assert user.unconfirmed_email == @new_email
assert_delivered_email(Email.User.send_email_reset_old_email(user))
assert_delivered_email(Email.User.send_email_reset_new_email(user))
conn
|> AbsintheHelpers.graphql_query(
query: @validate_email_mutation,
variables: %{token: user.confirmation_token}
)
user = Users.get_user!(user.id)
assert user.email == @new_email
assert user.unconfirmed_email == nil
end
test "change_email/3 with valid email but invalid token", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @old_email, password: @password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
res =
conn
|> AbsintheHelpers.graphql_query(
query: @login_mutation,
variables: %{email: @old_email, password: @password}
)
login = res["data"]["login"]
assert Map.has_key?(login, "accessToken") && not is_nil(login["accessToken"])
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @change_email_mutation,
variables: %{email: @new_email, password: @password}
)
assert res["errors"] == nil
assert res["data"]["changeEmail"]["id"] == to_string(user.id)
user = Users.get_user!(user.id)
assert user.email == @old_email
assert user.unconfirmed_email == @new_email
assert_delivered_email(Email.User.send_email_reset_old_email(user))
assert_delivered_email(Email.User.send_email_reset_new_email(user))
res =
conn
|> AbsintheHelpers.graphql_query(
query: @validate_email_mutation,
variables: %{token: "some token"}
)
assert hd(res["errors"])["message"] == "Invalid activation token"
user = Users.get_user!(user.id)
assert user.email == @old_email
assert user.unconfirmed_email == @new_email
end
test "change_email/3 with invalid password", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @old_email, password: @password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @change_email_mutation,
variables: %{email: @new_email, password: "invalid_password"}
)
assert hd(res["errors"])["message"] == "The password provided is invalid"
end
test "change_email/3 with same email", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @old_email, password: @password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @change_email_mutation,
variables: %{email: @old_email, password: @password}
)
assert hd(res["errors"])["message"] == "The new email must be different"
end
test "change_email/3 with invalid email", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @old_email, password: @password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @change_email_mutation,
variables: %{email: "invalid email", password: @password}
)
assert hd(res["errors"])["message"] == "The new email doesn't seem to be valid"
end
test "change_password/3 without being authenticated", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @old_email, password: @password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
res =
conn
|> AbsintheHelpers.graphql_query(
query: @change_email_mutation,
variables: %{email: @new_email, password: @password}
)
assert hd(res["errors"])["message"] ==
"You need to be logged-in to change your email"
end
end
describe "Resolver: User deletes it's account" do
@email "mail@domain.tld"
@password "p4ssw0rd"
test "delete_account/3 with valid password", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @email, password: @password})
# Hammer time !
{:ok, %User{} = user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
%Actor{} = actor1 = insert(:actor, user: user)
%Actor{} = actor2 = insert(:actor, user: user)
%Event{id: event_id} = event = insert(:event, organizer_actor: actor1)
%Participant{id: participant_id} =
insert(:participant, event: event, actor: actor2, role: :participant)
%Comment{id: comment_id} = insert(:comment, actor: actor2, event: event)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @delete_user_account_mutation,
variables: %{password: @password}
)
assert res["data"]["deleteAccount"]["id"] == to_string(user.id)
assert [
%Oban.Job{args: %{"actor_id" => actor2_id, "op" => "delete_actor"}},
%Oban.Job{args: %{"actor_id" => actor1_id, "op" => "delete_actor"}}
] = all_enqueued(queue: :background)
assert MapSet.new([actor1.id, actor2.id]) == MapSet.new([actor1_id, actor2_id])
assert is_nil(Users.get_user(user.id))
assert %{success: 2, failure: 0} == Oban.drain_queue(queue: :background)
assert_raise Ecto.NoResultsError, fn ->
Events.get_event!(event_id)
end
assert_raise Ecto.NoResultsError, fn ->
Discussions.get_comment!(comment_id)
end
# Actors are not deleted but emptied (to keep the username reserved)
actor1 = Actors.get_actor!(actor1_id)
assert actor1.suspended
assert is_nil(actor1.name)
actor2 = Actors.get_actor!(actor2_id)
assert actor2.suspended
assert is_nil(actor2.name)
assert is_nil(Events.get_participant(participant_id))
end
test "delete_account/3 with 3rd-party auth login", %{conn: conn} do
{:ok, %User{} = user} = Users.create_external(@email, "keycloak")
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(query: @delete_user_account_mutation)
assert is_nil(res["errors"])
assert res["data"]["deleteAccount"]["id"] == to_string(user.id)
end
test "delete_account/3 with invalid password", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @email, password: @password})
# Hammer time !
{:ok, %User{} = user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @delete_user_account_mutation,
variables: %{password: "invalid password"}
)
assert hd(res["errors"])["message"] == "The password provided is invalid"
end
test "delete_account/3 without being authenticated", %{conn: conn} do
{:ok, %User{} = user} = Users.register(%{email: @email, password: @password})
# Hammer time !
{:ok, %User{} = _user} =
Users.update_user(user, %{
confirmed_at: Timex.shift(user.confirmation_sent_at, hours: -3),
confirmation_sent_at: nil,
confirmation_token: nil
})
res =
conn
|> AbsintheHelpers.graphql_query(
query: @delete_user_account_mutation,
variables: %{password: "invalid password"}
)
assert hd(res["errors"])["message"] ==
"You need to be logged-in to delete your account"
end
end
end