Merge branch 'feature/edit-password' into 'master'
Feature/edit password Closes #165 See merge request framasoft/mobilizon!197
This commit is contained in:
commit
1adb1ab073
|
@ -25,6 +25,14 @@ mutation ValidateUser($token: String!) {
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const CHANGE_PASSWORD = gql`
|
||||||
|
mutation ChangePassword($oldPassword: String!, $newPassword: String!) {
|
||||||
|
changePassword(oldPassword: $oldPassword, newPassword: $newPassword) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const CURRENT_USER_CLIENT = gql`
|
export const CURRENT_USER_CLIENT = gql`
|
||||||
query {
|
query {
|
||||||
currentUser @client {
|
currentUser @client {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import SendPasswordReset from '@/views/User/SendPasswordReset.vue';
|
||||||
import PasswordReset from '@/views/User/PasswordReset.vue';
|
import PasswordReset from '@/views/User/PasswordReset.vue';
|
||||||
import { beforeRegisterGuard } from '@/router/guards/register-guard';
|
import { beforeRegisterGuard } from '@/router/guards/register-guard';
|
||||||
import { RouteConfig } from 'vue-router';
|
import { RouteConfig } from 'vue-router';
|
||||||
|
import PasswordChange from '@/views/User/PasswordChange.vue';
|
||||||
|
|
||||||
export enum UserRouteName {
|
export enum UserRouteName {
|
||||||
REGISTER = 'Register',
|
REGISTER = 'Register',
|
||||||
|
@ -16,6 +17,7 @@ export enum UserRouteName {
|
||||||
PASSWORD_RESET = 'PasswordReset',
|
PASSWORD_RESET = 'PasswordReset',
|
||||||
VALIDATE = 'Validate',
|
VALIDATE = 'Validate',
|
||||||
LOGIN = 'Login',
|
LOGIN = 'Login',
|
||||||
|
PASSWORD_CHANGE = 'PasswordChange',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const userRoutes: RouteConfig[] = [
|
export const userRoutes: RouteConfig[] = [
|
||||||
|
@ -70,4 +72,10 @@ export const userRoutes: RouteConfig[] = [
|
||||||
props: true,
|
props: true,
|
||||||
meta: { requiredAuth: false },
|
meta: { requiredAuth: false },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/my-account/password',
|
||||||
|
name: UserRouteName.PASSWORD_CHANGE,
|
||||||
|
component: PasswordChange,
|
||||||
|
meta: { requiredAuth: true },
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="container">
|
<section class="container">
|
||||||
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li class="is-active"><router-link :to="{ name: MyAccountRouteName.UPDATE_IDENTITY }" aria-current="page">{{ $t('My account') }}</router-link></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
<div v-if="currentActor">
|
<div v-if="currentActor">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<figure v-if="currentActor.banner" class="image is-3by1">
|
<figure v-if="currentActor.banner" class="image is-3by1">
|
||||||
|
@ -10,6 +15,9 @@
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="identities column is-4">
|
<div class="identities column is-4">
|
||||||
<identities v-bind:currentIdentityName="currentIdentityName"></identities>
|
<identities v-bind:currentIdentityName="currentIdentityName"></identities>
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button tag="router-link" type="is-secondary" :to="{ name: UserRouteName.PASSWORD_CHANGE }">{{ $t('Change password') }}</b-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-8">
|
<div class="column is-8">
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
|
@ -27,6 +35,10 @@
|
||||||
.identities {
|
.identities {
|
||||||
padding-right: 45px;
|
padding-right: 45px;
|
||||||
margin-right: 45px;
|
margin-right: 45px;
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
margin-top: 1.2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -36,6 +48,8 @@ import { Component, Vue, Watch } from 'vue-property-decorator';
|
||||||
import EventCard from '@/components/Event/EventCard.vue';
|
import EventCard from '@/components/Event/EventCard.vue';
|
||||||
import { IPerson } from '@/types/actor';
|
import { IPerson } from '@/types/actor';
|
||||||
import Identities from '@/components/Account/Identities.vue';
|
import Identities from '@/components/Account/Identities.vue';
|
||||||
|
import { UserRouteName } from '@/router/user';
|
||||||
|
import { MyAccountRouteName } from '@/router/actor';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
|
@ -52,6 +66,9 @@ export default class MyAccount extends Vue {
|
||||||
currentActor!: IPerson;
|
currentActor!: IPerson;
|
||||||
currentIdentityName: string | null = null;
|
currentIdentityName: string | null = null;
|
||||||
|
|
||||||
|
UserRouteName = UserRouteName;
|
||||||
|
MyAccountRouteName = MyAccountRouteName;
|
||||||
|
|
||||||
@Watch('$route.params.identityName', { immediate: true })
|
@Watch('$route.params.identityName', { immediate: true })
|
||||||
async onIdentityParamChanged (val: string) {
|
async onIdentityParamChanged (val: string) {
|
||||||
await this.redirectIfNoIdentitySelected(val);
|
await this.redirectIfNoIdentitySelected(val);
|
||||||
|
|
94
js/src/views/User/PasswordChange.vue
Normal file
94
js/src/views/User/PasswordChange.vue
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<template>
|
||||||
|
<section class="section">
|
||||||
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li><router-link :to="{ name: MyAccountRouteName.UPDATE_IDENTITY }">{{ $t('My account') }}</router-link></li>
|
||||||
|
<li class="is-active"><router-link :to="{ name: UserRouteName.PASSWORD_CHANGE }" aria-current="page">{{ $t('Password change') }}</router-link></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<h1 class="title">{{ $t('Password') }}</h1>
|
||||||
|
<b-notification
|
||||||
|
type="is-danger"
|
||||||
|
has-icon
|
||||||
|
aria-close-label="Close notification"
|
||||||
|
role="alert"
|
||||||
|
:key="error"
|
||||||
|
v-for="error in errors"
|
||||||
|
>
|
||||||
|
{{ error }}
|
||||||
|
</b-notification>
|
||||||
|
<form @submit="resetAction" class="form">
|
||||||
|
<b-field :label="$t('Old password')">
|
||||||
|
<b-input
|
||||||
|
aria-required="true"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
password-reveal
|
||||||
|
minlength="6"
|
||||||
|
v-model="oldPassword"
|
||||||
|
/>
|
||||||
|
</b-field>
|
||||||
|
<b-field :label="$t('New password')">
|
||||||
|
<b-input
|
||||||
|
aria-required="true"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
password-reveal
|
||||||
|
minlength="6"
|
||||||
|
v-model="newPassword"
|
||||||
|
/>
|
||||||
|
</b-field>
|
||||||
|
<button class="button is-primary">
|
||||||
|
{{ $t('Change my password') }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
|
import { CHANGE_PASSWORD } from '@/graphql/user';
|
||||||
|
import { UserRouteName } from '@/router/user';
|
||||||
|
import { MyAccountRouteName } from '@/router/actor';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class PasswordChange extends Vue {
|
||||||
|
oldPassword: string = '';
|
||||||
|
newPassword: string = '';
|
||||||
|
errors: string[] = [];
|
||||||
|
|
||||||
|
MyAccountRouteName = MyAccountRouteName;
|
||||||
|
UserRouteName = UserRouteName;
|
||||||
|
|
||||||
|
async resetAction(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.errors = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$apollo.mutate({
|
||||||
|
mutation: CHANGE_PASSWORD,
|
||||||
|
variables: {
|
||||||
|
oldPassword: this.oldPassword,
|
||||||
|
newPassword: this.newPassword,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$notifier.success(this.$t('The password was successfully changed') as string);
|
||||||
|
} catch (err) {
|
||||||
|
this.handleError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(err: any) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
if (err.graphQLErrors !== undefined) {
|
||||||
|
err.graphQLErrors.forEach(({ message }) => {
|
||||||
|
this.errors.push(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
</style>
|
|
@ -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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# source: http://localhost:4000/api
|
# source: http://localhost:4000/api
|
||||||
# timestamp: Fri Sep 20 2019 16:55:10 GMT+0200 (GMT+02:00)
|
# timestamp: Tue Sep 24 2019 18:20:05 GMT+0200 (GMT+02:00)
|
||||||
|
|
||||||
schema {
|
schema {
|
||||||
query: RootQueryType
|
query: RootQueryType
|
||||||
|
@ -184,7 +184,7 @@ enum CommentVisibility {
|
||||||
"""Visible only to people members of the group or followers of the person"""
|
"""Visible only to people members of the group or followers of the person"""
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
|
||||||
"""Publically listed and federated. Can be shared."""
|
"""Publicly listed and federated. Can be shared."""
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
|
||||||
"""Visible only to people with the link - or invited"""
|
"""Visible only to people with the link - or invited"""
|
||||||
|
@ -885,6 +885,9 @@ type RootMutationType {
|
||||||
"""Change default actor for user"""
|
"""Change default actor for user"""
|
||||||
changeDefaultActor(preferredUsername: String!): User
|
changeDefaultActor(preferredUsername: String!): User
|
||||||
|
|
||||||
|
"""Change an user password"""
|
||||||
|
changePassword(newPassword: String!, oldPassword: String!): User
|
||||||
|
|
||||||
"""Create a comment"""
|
"""Create a comment"""
|
||||||
createComment(actorUsername: String!, text: String!): Comment
|
createComment(actorUsername: String!, text: String!): Comment
|
||||||
|
|
||||||
|
@ -911,7 +914,7 @@ type RootMutationType {
|
||||||
"""The list of tags associated to the event"""
|
"""The list of tags associated to the event"""
|
||||||
tags: [String] = [""]
|
tags: [String] = [""]
|
||||||
title: String!
|
title: String!
|
||||||
visibility: EventVisibility = PRIVATE
|
visibility: EventVisibility = PUBLIC
|
||||||
): Event
|
): Event
|
||||||
|
|
||||||
"""Create a Feed Token"""
|
"""Create a Feed Token"""
|
||||||
|
@ -1044,7 +1047,7 @@ type RootMutationType {
|
||||||
description: String
|
description: String
|
||||||
endsOn: DateTime
|
endsOn: DateTime
|
||||||
eventId: ID!
|
eventId: ID!
|
||||||
joinOptions: EventJoinOptions
|
joinOptions: EventJoinOptions = FREE
|
||||||
onlineAddress: String
|
onlineAddress: String
|
||||||
options: EventOptionsInput
|
options: EventOptionsInput
|
||||||
phoneAddress: String
|
phoneAddress: String
|
||||||
|
@ -1059,7 +1062,7 @@ type RootMutationType {
|
||||||
"""The list of tags associated to the event"""
|
"""The list of tags associated to the event"""
|
||||||
tags: [String]
|
tags: [String]
|
||||||
title: String
|
title: String
|
||||||
visibility: EventVisibility
|
visibility: EventVisibility = PUBLIC
|
||||||
): Event
|
): Event
|
||||||
|
|
||||||
"""Update an identity"""
|
"""Update an identity"""
|
||||||
|
|
|
@ -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 a new issue