Allow empty values in PATH user service

This commit is contained in:
Matej Bačo
2024-01-20 09:43:31 +00:00
parent 8cc6bfdb9b
commit 311e9a75dd
6 changed files with 157 additions and 6 deletions
+14 -4
View File
@@ -1033,7 +1033,7 @@ App::patch('/v1/users/:userId/name')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User ID.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.')
->param('name', '', new Text(128, 0), 'User name. Max length: 128 chars.')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -1071,7 +1071,7 @@ App::patch('/v1/users/:userId/password')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User ID.')
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, enabled: $project->getAttribute('auths', [])['passwordDictionary'] ?? false, allowEmpty: true), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
->inject('response')
->inject('project')
->inject('dbForProject')
@@ -1091,6 +1091,16 @@ App::patch('/v1/users/:userId/password')
}
}
if (\strlen($password) === 0) {
$user
->setAttribute('password', '')
->setAttribute('passwordUpdate', DateTime::now());
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$queueForEvents->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_USER);
}
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
@@ -1136,7 +1146,7 @@ App::patch('/v1/users/:userId/email')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User ID.')
->param('email', '', new Email(), 'User email.')
->param('email', '', new Email(allowEmpty: true), 'User email.')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -1211,7 +1221,7 @@ App::patch('/v1/users/:userId/phone')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User ID.')
->param('number', '', new Phone(), 'User phone number.')
->param('number', '', new Phone(allowEmpty: true), 'User phone number.')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
+11
View File
@@ -11,6 +11,13 @@ use Utopia\Validator;
*/
class Password extends Validator
{
protected bool $allowEmpty;
public function __construct(bool $allowEmpty = false)
{
$this->allowEmpty = $allowEmpty;
}
/**
* Get Description.
*
@@ -36,6 +43,10 @@ class Password extends Validator
return false;
}
if ($this->allowEmpty && \strlen($value) === 0) {
return true;
}
if (\strlen($value) < 8) {
return false;
}
@@ -12,8 +12,9 @@ class PasswordDictionary extends Password
protected array $dictionary;
protected bool $enabled;
public function __construct(array $dictionary, bool $enabled = false)
public function __construct(array $dictionary, bool $enabled = false, bool $allowEmpty = false)
{
parent::__construct($allowEmpty);
$this->dictionary = $dictionary;
$this->enabled = $enabled;
}
+16 -1
View File
@@ -11,6 +11,13 @@ use Utopia\Validator;
*/
class Phone extends Validator
{
protected bool $allowEmpty;
public function __construct(bool $allowEmpty = false)
{
$this->allowEmpty = $allowEmpty;
}
/**
* Get Description.
*
@@ -32,7 +39,15 @@ class Phone extends Validator
*/
public function isValid($value): bool
{
return is_string($value) && !!\preg_match('/^\+[1-9]\d{1,14}$/', $value);
if (!is_string($value)) {
return false;
}
if ($this->allowEmpty && \strlen($value) === 0) {
return true;
}
return !!\preg_match('/^\+[1-9]\d{1,14}$/', $value);
}
/**
+11
View File
@@ -13,6 +13,13 @@ use Utopia\Validator;
*/
class Email extends Validator
{
protected bool $allowEmpty;
public function __construct(bool $allowEmpty = false)
{
$this->allowEmpty = $allowEmpty;
}
/**
* Get Description
*
@@ -35,6 +42,10 @@ class Email extends Validator
*/
public function isValid($value): bool
{
if ($this->allowEmpty && \strlen($value) === 0) {
return true;
}
if (!\filter_var($value, FILTER_VALIDATE_EMAIL)) {
return false;
}
+103
View File
@@ -742,6 +742,32 @@ trait UsersBase
/**
* Test for SUCCESS
*/
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertEquals($user['body']['name'], 'Cristiano Ronaldo');
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/name', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => '',
]);
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertEquals($user['body']['name'], '');
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertEquals($user['body']['name'], '');
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/name', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -809,6 +835,32 @@ trait UsersBase
/**
* Test for SUCCESS
*/
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertEquals($user['body']['email'], 'cristiano.ronaldo@manchester-united.co.uk');
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/email', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'email' => '',
]);
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertEquals($user['body']['email'], '');
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertEquals($user['body']['email'], '');
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/email', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -876,6 +928,37 @@ trait UsersBase
/**
* Test for SUCCESS
*/
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'email' => 'users.service@updated.com',
'password' => 'password'
]);
$this->assertEquals($session['headers']['status-code'], 201);
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/password', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'password' => '',
]);
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertNotEmpty($user['body']['$id']);
$this->assertEmpty($user['body']['password']);
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'email' => 'users.service@updated.com',
'password' => 'password'
]);
$this->assertEquals($session['headers']['status-code'], 401);
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/password', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -885,6 +968,7 @@ trait UsersBase
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertNotEmpty($user['body']['$id']);
$this->assertNotEmpty($user['body']['password']);
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
'content-type' => 'application/json',
@@ -1022,6 +1106,25 @@ trait UsersBase
/**
* Test for SUCCESS
*/
$updatedNumber = "";
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/phone', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'number' => $updatedNumber,
]);
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertEquals($user['body']['phone'], $updatedNumber);
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($user['headers']['status-code'], 200);
$this->assertEquals($user['body']['phone'], $updatedNumber);
$updatedNumber = "+910000000000"; //dummy number
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/phone', array_merge([
'content-type' => 'application/json',