[Backend] Allow to change your password
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
45a495a045
commit
4aaabced2c
@ -43,7 +43,9 @@ defmodule Mobilizon.Users.User do
|
|||||||
|
|
||||||
@registration_required_attrs [:email, :password]
|
@registration_required_attrs [:email, :password]
|
||||||
|
|
||||||
@password_reset_required_attrs [:password, :reset_password_token, :reset_password_sent_at]
|
@password_change_required_attrs [:password]
|
||||||
|
@password_reset_required_attrs @password_change_required_attrs ++
|
||||||
|
[:reset_password_token, :reset_password_sent_at]
|
||||||
|
|
||||||
@confirmation_token_length 30
|
@confirmation_token_length 30
|
||||||
|
|
||||||
@ -107,8 +109,22 @@ defmodule Mobilizon.Users.User do
|
|||||||
@doc false
|
@doc false
|
||||||
@spec password_reset_changeset(t, map) :: Ecto.Changeset.t()
|
@spec password_reset_changeset(t, map) :: Ecto.Changeset.t()
|
||||||
def password_reset_changeset(%__MODULE__{} = user, attrs) do
|
def password_reset_changeset(%__MODULE__{} = user, attrs) do
|
||||||
|
password_change_changeset(user, attrs, @password_reset_required_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Changeset to change a password
|
||||||
|
|
||||||
|
It checks the minimum requirements for a password and hashes it.
|
||||||
|
"""
|
||||||
|
@spec password_change_changeset(t, map) :: Ecto.Changeset.t()
|
||||||
|
def password_change_changeset(
|
||||||
|
%__MODULE__{} = user,
|
||||||
|
attrs,
|
||||||
|
required_attrs \\ @password_change_required_attrs
|
||||||
|
) do
|
||||||
user
|
user
|
||||||
|> cast(attrs, @password_reset_required_attrs)
|
|> cast(attrs, required_attrs)
|
||||||
|> validate_length(:password,
|
|> validate_length(:password,
|
||||||
min: 6,
|
min: 6,
|
||||||
max: 100,
|
max: 100,
|
||||||
|
@ -7,6 +7,7 @@ defmodule MobilizonWeb.Resolvers.User do
|
|||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
alias Mobilizon.Service.Users.{ResetPassword, Activation}
|
alias Mobilizon.Service.Users.{ResetPassword, Activation}
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
alias Mobilizon.Storage.Repo
|
||||||
|
|
||||||
import Mobilizon.Users.Guards
|
import Mobilizon.Users.Guards
|
||||||
|
|
||||||
@ -238,4 +239,34 @@ defmodule MobilizonWeb.Resolvers.User do
|
|||||||
{:ok, participations}
|
{:ok, participations}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def change_password(_parent, %{old_password: old_password, new_password: new_password}, %{
|
||||||
|
context: %{current_user: %User{password_hash: old_password_hash} = user}
|
||||||
|
}) do
|
||||||
|
with {:current_password, true} <-
|
||||||
|
{:current_password, Argon2.verify_pass(old_password, old_password_hash)},
|
||||||
|
{:same_password, false} <- {:same_password, old_password == new_password},
|
||||||
|
{:ok, %User{} = user} <-
|
||||||
|
user
|
||||||
|
|> User.password_change_changeset(%{
|
||||||
|
"password" => new_password
|
||||||
|
})
|
||||||
|
|> Repo.update() do
|
||||||
|
{:ok, user}
|
||||||
|
else
|
||||||
|
{:current_password, false} ->
|
||||||
|
{:error, "The current password is invalid"}
|
||||||
|
|
||||||
|
{:same_password, true} ->
|
||||||
|
{:error, "The new password must be different"}
|
||||||
|
|
||||||
|
{:error, %Ecto.Changeset{errors: [password: {"registration.error.password_too_short", _}]}} ->
|
||||||
|
{:error,
|
||||||
|
"The password you have chosen is too short. Please make sure your password contains at least 6 characters."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def change_password(_parent, _args, _resolution) do
|
||||||
|
{:error, "You need to be logged-in to change your password"}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -159,5 +159,12 @@ defmodule MobilizonWeb.Schema.UserType do
|
|||||||
arg(:preferred_username, non_null(:string))
|
arg(:preferred_username, non_null(:string))
|
||||||
resolve(&User.change_default_actor/3)
|
resolve(&User.change_default_actor/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@desc "Change an user password"
|
||||||
|
field :change_password, :user do
|
||||||
|
arg(:old_password, non_null(:string))
|
||||||
|
arg(:new_password, non_null(:string))
|
||||||
|
resolve(&User.change_password/3)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -833,4 +833,193 @@ defmodule MobilizonWeb.Resolvers.UserResolverTest do
|
|||||||
] == actor2.preferred_username
|
] == actor2.preferred_username
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user