Skip to content

Commit caa77f1

Browse files
author
Valeriy Naida
authored
ENGCOM-3004: [Recheck] Allow changing logged in customers password with Graphql mutation #187
2 parents dc5b402 + 29ff3f9 commit caa77f1

File tree

3 files changed

+236
-0
lines changed

3 files changed

+236
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Resolver\Customer\Account;
9+
10+
use Magento\Authorization\Model\UserContextInterface;
11+
use Magento\Customer\Api\AccountManagementInterface;
12+
use Magento\Customer\Model\Customer;
13+
use Magento\CustomerGraphQl\Model\Resolver\Customer\CustomerDataProvider;
14+
use Magento\Framework\GraphQl\Config\Element\Field;
15+
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
16+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
17+
use Magento\Framework\GraphQl\Query\ResolverInterface;
18+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
19+
20+
/**
21+
* @inheritdoc
22+
*/
23+
class ChangePassword implements ResolverInterface
24+
{
25+
/**
26+
* @var UserContextInterface
27+
*/
28+
private $userContext;
29+
30+
/**
31+
* @var AccountManagementInterface
32+
*/
33+
private $accountManagement;
34+
35+
/**
36+
* @var CustomerDataProvider
37+
*/
38+
private $customerResolver;
39+
40+
/**
41+
* @param UserContextInterface $userContext
42+
* @param AccountManagementInterface $accountManagement
43+
* @param CustomerDataProvider $customerResolver
44+
*/
45+
public function __construct(
46+
UserContextInterface $userContext,
47+
AccountManagementInterface $accountManagement,
48+
CustomerDataProvider $customerResolver
49+
) {
50+
$this->userContext = $userContext;
51+
$this->accountManagement = $accountManagement;
52+
$this->customerResolver = $customerResolver;
53+
}
54+
55+
/**
56+
* @inheritdoc
57+
*/
58+
public function resolve(
59+
Field $field,
60+
$context,
61+
ResolveInfo $info,
62+
array $value = null,
63+
array $args = null
64+
) {
65+
if (!isset($args['currentPassword'])) {
66+
throw new GraphQlInputException(__('"currentPassword" value should be specified'));
67+
}
68+
69+
if (!isset($args['newPassword'])) {
70+
throw new GraphQlInputException(__('"newPassword" value should be specified'));
71+
}
72+
73+
$customerId = (int)$this->userContext->getUserId();
74+
if ($customerId === 0) {
75+
throw new GraphQlAuthorizationException(
76+
__(
77+
'Current customer does not have access to the resource "%1"',
78+
[Customer::ENTITY]
79+
)
80+
);
81+
}
82+
83+
$this->accountManagement->changePasswordById($customerId, $args['currentPassword'], $args['newPassword']);
84+
$data = $this->customerResolver->getCustomerById($customerId);
85+
86+
return $data;
87+
}
88+
}

app/code/Magento/CustomerGraphQl/etc/schema.graphqls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type Query {
77

88
type Mutation {
99
generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\GenerateCustomerToken") @doc(description:"Retrieve Customer token")
10+
changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\ChangePassword") @doc(description:"Changes password for logged in customer")
1011
}
1112

1213
type CustomerToken {
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\Customer;
9+
10+
use Magento\Customer\Api\AccountManagementInterface;
11+
use Magento\Customer\Model\CustomerRegistry;
12+
use Magento\Framework\Exception\LocalizedException;
13+
use Magento\Integration\Api\CustomerTokenServiceInterface;
14+
use Magento\TestFramework\Helper\Bootstrap;
15+
use Magento\TestFramework\TestCase\GraphQlAbstract;
16+
17+
class CustomerChangePasswordTest extends GraphQlAbstract
18+
{
19+
/**
20+
* @var AccountManagementInterface
21+
*/
22+
private $accountManagement;
23+
24+
/**
25+
* @var CustomerTokenServiceInterface
26+
*/
27+
private $customerTokenService;
28+
29+
/**
30+
* @var CustomerRegistry
31+
*/
32+
private $customerRegistry;
33+
34+
protected function setUp()
35+
{
36+
$this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
37+
$this->accountManagement = Bootstrap::getObjectManager()->get(AccountManagementInterface::class);
38+
$this->customerRegistry = Bootstrap::getObjectManager()->get(CustomerRegistry::class);
39+
}
40+
41+
/**
42+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
43+
*/
44+
public function testCustomerChangeValidPassword()
45+
{
46+
$customerEmail = '[email protected]';
47+
$oldCustomerPassword = 'password';
48+
$newCustomerPassword = 'anotherPassword1';
49+
50+
$query = $this->getChangePassQuery($oldCustomerPassword, $newCustomerPassword);
51+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword);
52+
53+
$response = $this->graphQlQuery($query, [], '', $headerMap);
54+
$this->assertEquals($customerEmail, $response['changeCustomerPassword']['email']);
55+
56+
try {
57+
// registry contains the old password hash so needs to be reset
58+
$this->customerRegistry->removeByEmail($customerEmail);
59+
$this->accountManagement->authenticate($customerEmail, $newCustomerPassword);
60+
} catch (LocalizedException $e) {
61+
$this->fail('Password was not changed: ' . $e->getMessage());
62+
}
63+
}
64+
65+
public function testGuestUserCannotChangePassword()
66+
{
67+
$query = $this->getChangePassQuery('currentpassword', 'newpassword');
68+
$this->expectException(\Exception::class);
69+
$this->expectExceptionMessage(
70+
'GraphQL response contains errors: Current customer' . ' ' .
71+
'does not have access to the resource "customer"'
72+
);
73+
$this->graphQlQuery($query);
74+
}
75+
76+
/**
77+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
78+
*/
79+
public function testChangeWeakPassword()
80+
{
81+
$this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/190');
82+
$customerEmail = '[email protected]';
83+
$oldCustomerPassword = 'password';
84+
$newCustomerPassword = 'weakpass';
85+
86+
$query = $this->getChangePassQuery($oldCustomerPassword, $newCustomerPassword);
87+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword);
88+
89+
$this->expectException(\Exception::class);
90+
$this->expectExceptionMessageRegExp('/Minimum of different classes of characters in password is.*/');
91+
92+
$this->graphQlQuery($query, [], '', $headerMap);
93+
}
94+
95+
/**
96+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
97+
*/
98+
public function testCannotChangeWithIncorrectPassword()
99+
{
100+
$this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/190');
101+
$customerEmail = '[email protected]';
102+
$oldCustomerPassword = 'password';
103+
$newCustomerPassword = 'anotherPassword1';
104+
$incorrectPassword = 'password-incorrect';
105+
106+
$query = $this->getChangePassQuery($incorrectPassword, $newCustomerPassword);
107+
108+
// acquire authentication with correct password
109+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword);
110+
111+
$this->expectException(\Exception::class);
112+
$this->expectExceptionMessageRegExp('/The password doesn\'t match this account. Verify the password.*/');
113+
114+
// but try to change with incorrect 'old' password
115+
$this->graphQlQuery($query, [], '', $headerMap);
116+
}
117+
118+
private function getChangePassQuery($currentPassword, $newPassword)
119+
{
120+
$query = <<<QUERY
121+
mutation {
122+
changeCustomerPassword(
123+
currentPassword: "$currentPassword",
124+
newPassword: "$newPassword"
125+
) {
126+
id
127+
email
128+
firstname
129+
lastname
130+
}
131+
}
132+
QUERY;
133+
134+
return $query;
135+
}
136+
137+
/**
138+
* @param string $email
139+
* @param string $password
140+
* @return array
141+
*/
142+
private function getCustomerAuthHeaders(string $email, string $password): array
143+
{
144+
$customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password);
145+
return ['Authorization' => 'Bearer ' . $customerToken];
146+
}
147+
}

0 commit comments

Comments
 (0)