Merge branch 'cl-1.4.x' of https://github.com/appwrite/appwrite into feat-implement-migrations
This commit is contained in:
commit
7d60e4272d
3
.env
3
.env
|
@ -81,4 +81,5 @@ _APP_DOCKER_HUB_PASSWORD=
|
||||||
_APP_CONSOLE_GITHUB_SECRET=
|
_APP_CONSOLE_GITHUB_SECRET=
|
||||||
_APP_CONSOLE_GITHUB_APP_ID=
|
_APP_CONSOLE_GITHUB_APP_ID=
|
||||||
_APP_MIGRATIONS_FIREBASE_CLIENT_ID=
|
_APP_MIGRATIONS_FIREBASE_CLIENT_ID=
|
||||||
_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=
|
_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=
|
||||||
|
OPENAI_API_KEY=YOUR_OPENAI_API_KEY
|
||||||
|
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,4 +1,4 @@
|
||||||
[submodule "app/console"]
|
[submodule "app/console"]
|
||||||
path = app/console
|
path = app/console
|
||||||
url = https://github.com/appwrite/console
|
url = https://github.com/appwrite/console
|
||||||
branch = 2.3.4
|
branch = disallow-personal-data
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
- Add error attribute to indexes and attributes [#4575](https://github.com/appwrite/appwrite/pull/4575)
|
- Add error attribute to indexes and attributes [#4575](https://github.com/appwrite/appwrite/pull/4575)
|
||||||
- Add new index validation rules [#5710](https://github.com/appwrite/appwrite/pull/5710)
|
- Add new index validation rules [#5710](https://github.com/appwrite/appwrite/pull/5710)
|
||||||
|
- Added support for disallowing passwords that contain personal data [#5371](https://github.com/appwrite/appwrite/pull/5371)
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
|
|
||||||
|
|
|
@ -175,6 +175,16 @@ return [
|
||||||
'description' => 'Passwords do not match. Please check the password and confirm password.',
|
'description' => 'Passwords do not match. Please check the password and confirm password.',
|
||||||
'code' => 400,
|
'code' => 400,
|
||||||
],
|
],
|
||||||
|
Exception::USER_PASSWORD_RECENTLY_USED => [
|
||||||
|
'name' => Exception::USER_PASSWORD_RECENTLY_USED,
|
||||||
|
'description' => 'The password you are trying to use is similar to your previous password. Please choose a stronger password.',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
Exception::USER_PASSWORD_PERSONAL_DATA => [
|
||||||
|
'name' => Exception::USER_PASSWORD_PERSONAL_DATA,
|
||||||
|
'description' => 'The password you are trying to use contains references to your name, email, phone or userID. Please choose a different password and try again.',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
Exception::USER_SESSION_NOT_FOUND => [
|
Exception::USER_SESSION_NOT_FOUND => [
|
||||||
'name' => Exception::USER_SESSION_NOT_FOUND,
|
'name' => Exception::USER_SESSION_NOT_FOUND,
|
||||||
'description' => 'The current user session could not be found.',
|
'description' => 'The current user session could not be found.',
|
||||||
|
|
1
app/config/specs/open-api3-1.4.x-client.json
Normal file
1
app/config/specs/open-api3-1.4.x-client.json
Normal file
File diff suppressed because one or more lines are too long
1
app/config/specs/open-api3-1.4.x-console.json
Normal file
1
app/config/specs/open-api3-1.4.x-console.json
Normal file
File diff suppressed because one or more lines are too long
1
app/config/specs/open-api3-1.4.x-server.json
Normal file
1
app/config/specs/open-api3-1.4.x-server.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/config/specs/swagger2-1.4.x-client.json
Normal file
1
app/config/specs/swagger2-1.4.x-client.json
Normal file
File diff suppressed because one or more lines are too long
1
app/config/specs/swagger2-1.4.x-console.json
Normal file
1
app/config/specs/swagger2-1.4.x-console.json
Normal file
File diff suppressed because one or more lines are too long
1
app/config/specs/swagger2-1.4.x-server.json
Normal file
1
app/config/specs/swagger2-1.4.x-server.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -31,6 +31,8 @@ return [
|
||||||
'audio/ogg', // Ogg Vorbis RFC 5334
|
'audio/ogg', // Ogg Vorbis RFC 5334
|
||||||
'audio/vorbis', // Vorbis RFC 5215
|
'audio/vorbis', // Vorbis RFC 5215
|
||||||
'audio/vnd.wav', // wav RFC 2361
|
'audio/vnd.wav', // wav RFC 2361
|
||||||
|
'audio/aac', //AAC audio
|
||||||
|
'audio/x-hx-aac-adts', // AAC audio
|
||||||
|
|
||||||
// Microsoft Word
|
// Microsoft Word
|
||||||
'application/msword',
|
'application/msword',
|
||||||
|
|
|
@ -43,6 +43,7 @@ use Utopia\Validator\Text;
|
||||||
use Utopia\Validator\WhiteList;
|
use Utopia\Validator\WhiteList;
|
||||||
use Appwrite\Auth\Validator\PasswordHistory;
|
use Appwrite\Auth\Validator\PasswordHistory;
|
||||||
use Appwrite\Auth\Validator\PasswordDictionary;
|
use Appwrite\Auth\Validator\PasswordDictionary;
|
||||||
|
use Appwrite\Auth\Validator\PersonalData;
|
||||||
|
|
||||||
$oauthDefaultSuccess = '/auth/oauth2/success';
|
$oauthDefaultSuccess = '/auth/oauth2/success';
|
||||||
$oauthDefaultFailure = '/auth/oauth2/failure';
|
$oauthDefaultFailure = '/auth/oauth2/failure';
|
||||||
|
@ -194,6 +195,13 @@ App::post('/v1/account')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
|
||||||
|
$personalDataValidator = new PersonalData($userId, $email, $name, null);
|
||||||
|
if (!$personalDataValidator->isValid($password)) {
|
||||||
|
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
$passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||||
$password = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
$password = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||||
try {
|
try {
|
||||||
|
@ -838,7 +846,7 @@ App::post('/v1/account/sessions/magic-url')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$userId = $userId == 'unique()' ? ID::unique() : $userId;
|
$userId = $userId === 'unique()' ? ID::unique() : $userId;
|
||||||
|
|
||||||
$user->setAttributes([
|
$user->setAttributes([
|
||||||
'$id' => $userId,
|
'$id' => $userId,
|
||||||
|
@ -1091,7 +1099,7 @@ App::post('/v1/account/sessions/phone')
|
||||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
->label('sdk.response.model', Response::MODEL_TOKEN)
|
->label('sdk.response.model', Response::MODEL_TOKEN)
|
||||||
->label('abuse-limit', 10)
|
->label('abuse-limit', 10)
|
||||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
->label('abuse-key', 'url:{url},email:{param-phone}')
|
||||||
->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||||
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||||
->inject('request')
|
->inject('request')
|
||||||
|
@ -1756,16 +1764,22 @@ App::patch('/v1/account/password')
|
||||||
|
|
||||||
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||||
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||||
$history = [];
|
$history = $user->getAttribute('passwordHistory', []);
|
||||||
if ($historyLimit > 0) {
|
if ($historyLimit > 0) {
|
||||||
$history = $user->getAttribute('passwordHistory', []);
|
|
||||||
$validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
$validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||||
if (!$validator->isValid($password)) {
|
if (!$validator->isValid($password)) {
|
||||||
throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED, 'The password was recently used', 409);
|
throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED);
|
||||||
}
|
}
|
||||||
|
|
||||||
$history[] = $newPassword;
|
$history[] = $newPassword;
|
||||||
array_slice($history, (count($history) - $historyLimit), $historyLimit);
|
$history = array_slice($history, (count($history) - $historyLimit), $historyLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
|
||||||
|
$personalDataValidator = new PersonalData($user->getId(), $user->getAttribute('email'), $user->getAttribute('name'), $user->getAttribute('phone'));
|
||||||
|
if (!$personalDataValidator->isValid($password)) {
|
||||||
|
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$user
|
$user
|
||||||
|
@ -2360,8 +2374,9 @@ App::put('/v1/account/recovery')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
|
->inject('project')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Document $user, Database $dbForProject, Event $events) {
|
->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Document $user, Database $dbForProject, Document $project, Event $events) {
|
||||||
if ($password !== $passwordAgain) {
|
if ($password !== $passwordAgain) {
|
||||||
throw new Exception(Exception::USER_PASSWORD_MISMATCH);
|
throw new Exception(Exception::USER_PASSWORD_MISMATCH);
|
||||||
}
|
}
|
||||||
|
@ -2381,8 +2396,23 @@ App::put('/v1/account/recovery')
|
||||||
|
|
||||||
Authorization::setRole(Role::user($profile->getId())->toString());
|
Authorization::setRole(Role::user($profile->getId())->toString());
|
||||||
|
|
||||||
|
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||||
|
|
||||||
|
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||||
|
$history = $profile->getAttribute('passwordHistory', []);
|
||||||
|
if ($historyLimit > 0) {
|
||||||
|
$validator = new PasswordHistory($history, $profile->getAttribute('hash'), $profile->getAttribute('hashOptions'));
|
||||||
|
if (!$validator->isValid($password)) {
|
||||||
|
throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED);
|
||||||
|
}
|
||||||
|
|
||||||
|
$history[] = $newPassword;
|
||||||
|
$history = array_slice($history, (count($history) - $historyLimit), $historyLimit);
|
||||||
|
}
|
||||||
|
|
||||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile
|
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile
|
||||||
->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS))
|
->setAttribute('password', $newPassword)
|
||||||
|
->setAttribute('passwordHistory', $history)
|
||||||
->setAttribute('passwordUpdate', DateTime::now())
|
->setAttribute('passwordUpdate', DateTime::now())
|
||||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS)
|
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS)
|
||||||
|
|
|
@ -4,6 +4,7 @@ use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
use Utopia\App;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Validator\Text;
|
||||||
|
|
||||||
App::init()
|
App::init()
|
||||||
->groups(['console'])
|
->groups(['console'])
|
||||||
|
@ -38,3 +39,58 @@ App::get('/v1/console/variables')
|
||||||
|
|
||||||
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
|
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
App::post('/v1/console/assistant')
|
||||||
|
->desc('Ask Query')
|
||||||
|
->groups(['api', 'assistant'])
|
||||||
|
->label('scope', 'public')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
||||||
|
->label('sdk.namespace', 'assistant')
|
||||||
|
->label('sdk.method', 'chat')
|
||||||
|
->label('sdk.description', '/docs/references/assistant/chat.md')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_TEXT)
|
||||||
|
->label('abuse-limit', 15)
|
||||||
|
->label('abuse-key', 'userId:{userId}')
|
||||||
|
->param('query', '', new Text(2000), 'Query')
|
||||||
|
->inject('response')
|
||||||
|
->action(function (string $query, Response $response) {
|
||||||
|
$ch = curl_init('http://appwrite-assistant:3003/');
|
||||||
|
$responseHeaders = [];
|
||||||
|
$query = json_encode(['prompt' => $query]);
|
||||||
|
$headers = ['accept: text/event-stream'];
|
||||||
|
$handleEvent = function ($ch, $data) use ($response) {
|
||||||
|
$response->chunk($data);
|
||||||
|
|
||||||
|
return \strlen($data);
|
||||||
|
};
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $handleEvent);
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 9000);
|
||||||
|
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) {
|
||||||
|
$len = strlen($header);
|
||||||
|
$header = explode(':', $header, 2);
|
||||||
|
|
||||||
|
if (count($header) < 2) { // ignore invalid headers
|
||||||
|
return $len;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
|
||||||
|
|
||||||
|
return $len;
|
||||||
|
});
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
|
||||||
|
|
||||||
|
curl_exec($ch);
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$response->chunk('', true);
|
||||||
|
});
|
||||||
|
|
|
@ -88,7 +88,7 @@ App::post('/v1/projects')
|
||||||
}
|
}
|
||||||
|
|
||||||
$auth = Config::getParam('auth', []);
|
$auth = Config::getParam('auth', []);
|
||||||
$auths = ['limit' => 0, 'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT, 'passwordHistory' => 0, 'passwordDictionary' => false, 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG];
|
$auths = ['limit' => 0, 'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT, 'passwordHistory' => 0, 'passwordDictionary' => false, 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, 'personalDataCheck' => false];
|
||||||
foreach ($auth as $index => $method) {
|
foreach ($auth as $index => $method) {
|
||||||
$auths[$method['key'] ?? ''] = true;
|
$auths[$method['key'] ?? ''] = true;
|
||||||
}
|
}
|
||||||
|
@ -615,7 +615,7 @@ App::patch('/v1/projects/:projectId/auth/password-history')
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||||
->desc('Update authentication password disctionary status. Use this endpoint to enable or disable the dicitonary check for user password')
|
->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
@ -645,6 +645,37 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::patch('/v1/projects/:projectId/auth/personal-data')
|
||||||
|
->desc('Enable or disable checking user passwords for similarity with their personal data.')
|
||||||
|
->groups(['api', 'projects'])
|
||||||
|
->label('scope', 'projects.write')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'projects')
|
||||||
|
->label('sdk.method', 'updatePersonalDataCheck')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_PROJECT)
|
||||||
|
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||||
|
->param('enabled', false, new Boolean(false), 'Set whether or not to check a password for similarity with personal data. Default is false.')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForConsole')
|
||||||
|
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
|
if ($project->isEmpty()) {
|
||||||
|
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$auths = $project->getAttribute('auths', []);
|
||||||
|
$auths['personalDataCheck'] = $enabled;
|
||||||
|
|
||||||
|
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||||
|
->setAttribute('auths', $auths));
|
||||||
|
|
||||||
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/max-sessions')
|
App::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||||
->desc('Update Project user sessions limit')
|
->desc('Update Project user sessions limit')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
|
|
|
@ -37,6 +37,7 @@ use MaxMind\Db\Reader;
|
||||||
use Utopia\Validator\Integer;
|
use Utopia\Validator\Integer;
|
||||||
use Appwrite\Auth\Validator\PasswordHistory;
|
use Appwrite\Auth\Validator\PasswordHistory;
|
||||||
use Appwrite\Auth\Validator\PasswordDictionary;
|
use Appwrite\Auth\Validator\PasswordDictionary;
|
||||||
|
use Appwrite\Auth\Validator\PersonalData;
|
||||||
|
|
||||||
/** TODO: Remove function when we move to using utopia/platform */
|
/** TODO: Remove function when we move to using utopia/platform */
|
||||||
function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $events): Document
|
function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $events): Document
|
||||||
|
@ -53,6 +54,13 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||||
? ID::unique()
|
? ID::unique()
|
||||||
: ID::custom($userId);
|
: ID::custom($userId);
|
||||||
|
|
||||||
|
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
|
||||||
|
$personalDataValidator = new PersonalData($userId, $email, $name, $phone);
|
||||||
|
if (!$personalDataValidator->isValid($password)) {
|
||||||
|
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
|
$password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
|
||||||
$user = $dbForProject->createDocument('users', new Document([
|
$user = $dbForProject->createDocument('users', new Document([
|
||||||
'$id' => $userId,
|
'$id' => $userId,
|
||||||
|
@ -825,19 +833,25 @@ App::patch('/v1/users/:userId/password')
|
||||||
throw new Exception(Exception::USER_NOT_FOUND);
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
|
||||||
|
$personalDataValidator = new PersonalData($userId, $user->getAttribute('email'), $user->getAttribute('name'), $user->getAttribute('phone'));
|
||||||
|
if (!$personalDataValidator->isValid($password)) {
|
||||||
|
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||||
|
|
||||||
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||||
$history = [];
|
$history = $user->getAttribute('passwordHistory', []);
|
||||||
if ($historyLimit > 0) {
|
if ($historyLimit > 0) {
|
||||||
$history = $user->getAttribute('passwordHistory', []);
|
|
||||||
$validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
$validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||||
if (!$validator->isValid($password)) {
|
if (!$validator->isValid($password)) {
|
||||||
throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED, 'The password was recently used', 409);
|
throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED);
|
||||||
}
|
}
|
||||||
|
|
||||||
$history[] = $newPassword;
|
$history[] = $newPassword;
|
||||||
array_slice($history, (count($history) - $historyLimit), $historyLimit);
|
$history = array_slice($history, (count($history) - $historyLimit), $historyLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user
|
$user
|
||||||
|
|
|
@ -29,7 +29,7 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
|
||||||
$parts = explode('.', $match);
|
$parts = explode('.', $match);
|
||||||
|
|
||||||
if (count($parts) !== 2) {
|
if (count($parts) !== 2) {
|
||||||
throw new Exception('Too less or too many parts', 400, Exception::GENERAL_ARGUMENT_INVALID);
|
throw new Exception(Exception::GENERAL_SERVER_ERROR, "The server encountered an error while parsing the label: $label. Please create an issue on GitHub to allow us to investigate further https://github.com/appwrite/appwrite/issues/new/choose");
|
||||||
}
|
}
|
||||||
|
|
||||||
$namespace = $parts[0] ?? '';
|
$namespace = $parts[0] ?? '';
|
||||||
|
|
|
@ -36,7 +36,6 @@ use Utopia\App;
|
||||||
use Utopia\Logger\Logger;
|
use Utopia\Logger\Logger;
|
||||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||||
use Utopia\Cache\Cache;
|
use Utopia\Cache\Cache;
|
||||||
use Utopia\CLI\Console;
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
|
@ -817,7 +816,6 @@ foreach ($locales as $locale) {
|
||||||
if (!\file_exists($path)) {
|
if (!\file_exists($path)) {
|
||||||
$path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar`
|
$path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar`
|
||||||
if (!\file_exists($path)) {
|
if (!\file_exists($path)) {
|
||||||
var_dump('Unable to find tralsnations for ' . $locale['code'] . ' so using en.json');
|
|
||||||
$path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json`
|
$path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
10
composer.lock
generated
10
composer.lock
generated
|
@ -2504,12 +2504,12 @@
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/transfer.git",
|
"url": "https://github.com/utopia-php/transfer.git",
|
||||||
"reference": "034c2dcacd4aa2595bfff84f11776dce66cf9153"
|
"reference": "7fca998f19e2d12dcfa003d7534e4ae1c2f205a1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/transfer/zipball/034c2dcacd4aa2595bfff84f11776dce66cf9153",
|
"url": "https://api.github.com/repos/utopia-php/transfer/zipball/7fca998f19e2d12dcfa003d7534e4ae1c2f205a1",
|
||||||
"reference": "034c2dcacd4aa2595bfff84f11776dce66cf9153",
|
"reference": "7fca998f19e2d12dcfa003d7534e4ae1c2f205a1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -2566,7 +2566,7 @@
|
||||||
"source": "https://github.com/utopia-php/transfer/tree/feat-improve-features",
|
"source": "https://github.com/utopia-php/transfer/tree/feat-improve-features",
|
||||||
"issues": "https://github.com/utopia-php/transfer/issues"
|
"issues": "https://github.com/utopia-php/transfer/issues"
|
||||||
},
|
},
|
||||||
"time": "2023-08-08T14:38:06+00:00"
|
"time": "2023-08-09T11:33:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/websocket",
|
"name": "utopia-php/websocket",
|
||||||
|
@ -5391,5 +5391,5 @@
|
||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "8.0"
|
"php": "8.0"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.1.0"
|
"plugin-api-version": "2.3.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -727,6 +727,14 @@ services:
|
||||||
- _APP_CONNECTIONS_QUEUE
|
- _APP_CONNECTIONS_QUEUE
|
||||||
- _APP_REGION
|
- _APP_REGION
|
||||||
|
|
||||||
|
appwrite-assistant:
|
||||||
|
container_name: appwrite-assistant
|
||||||
|
image: appwrite/assistant:0.1.0
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
environment:
|
||||||
|
- OPENAI_API_KEY
|
||||||
|
|
||||||
openruntimes-executor:
|
openruntimes-executor:
|
||||||
container_name: openruntimes-executor
|
container_name: openruntimes-executor
|
||||||
hostname: exc1
|
hostname: exc1
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 458 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.8 KiB |
102
src/Appwrite/Auth/Validator/PersonalData.php
Normal file
102
src/Appwrite/Auth/Validator/PersonalData.php
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Auth\Validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates user password string against their personal data
|
||||||
|
*/
|
||||||
|
class PersonalData extends Password
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected ?string $userId = null,
|
||||||
|
protected ?string $email = null,
|
||||||
|
protected ?string $name = null,
|
||||||
|
protected ?string $phone = null,
|
||||||
|
protected bool $strict = false
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Description.
|
||||||
|
*
|
||||||
|
* Returns validator description
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Password must not include any personal data like your name, email, phone number, etc.';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is valid.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isValid($password): bool
|
||||||
|
{
|
||||||
|
if (!parent::isValid($password)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->strict) {
|
||||||
|
$password = strtolower($password);
|
||||||
|
$this->userId = strtolower($this->userId);
|
||||||
|
$this->email = strtolower($this->email);
|
||||||
|
$this->name = strtolower($this->name);
|
||||||
|
$this->phone = strtolower($this->phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->userId && strpos($password, $this->userId) !== false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->email && strpos($password, $this->email) !== false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->email && strpos($password, explode('@', $this->email)[0] ?? '') !== false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->name && strpos($password, $this->name) !== false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->phone && strpos($password, str_replace('+', '', $this->phone)) !== false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->phone && strpos($password, $this->phone) !== false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is array
|
||||||
|
*
|
||||||
|
* Function will return true if object is array.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isArray(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* Returns validator type.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return self::TYPE_STRING;
|
||||||
|
}
|
||||||
|
}
|
|
@ -70,6 +70,7 @@ class Exception extends \Exception
|
||||||
public const USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists';
|
public const USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists';
|
||||||
public const USER_NOT_FOUND = 'user_not_found';
|
public const USER_NOT_FOUND = 'user_not_found';
|
||||||
public const USER_PASSWORD_RECENTLY_USED = 'password_recently_used';
|
public const USER_PASSWORD_RECENTLY_USED = 'password_recently_used';
|
||||||
|
public const USER_PASSWORD_PERSONAL_DATA = 'password_personal_data';
|
||||||
public const USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists';
|
public const USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists';
|
||||||
public const USER_PASSWORD_MISMATCH = 'user_password_mismatch';
|
public const USER_PASSWORD_MISMATCH = 'user_password_mismatch';
|
||||||
public const USER_SESSION_NOT_FOUND = 'user_session_not_found';
|
public const USER_SESSION_NOT_FOUND = 'user_session_not_found';
|
||||||
|
|
|
@ -132,6 +132,12 @@ class Project extends Model
|
||||||
'default' => false,
|
'default' => false,
|
||||||
'example' => true,
|
'example' => true,
|
||||||
])
|
])
|
||||||
|
->addRule('authPersonalDataCheck', [
|
||||||
|
'type' => self::TYPE_BOOLEAN,
|
||||||
|
'description' => 'Whether or not to check the user password for similarity with their personal data.',
|
||||||
|
'default' => false,
|
||||||
|
'example' => true,
|
||||||
|
])
|
||||||
->addRule('providers', [
|
->addRule('providers', [
|
||||||
'type' => Response::MODEL_PROVIDER,
|
'type' => Response::MODEL_PROVIDER,
|
||||||
'description' => 'List of Providers.',
|
'description' => 'List of Providers.',
|
||||||
|
@ -307,6 +313,7 @@ class Project extends Model
|
||||||
$document->setAttribute('authSessionsLimit', $authValues['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT);
|
$document->setAttribute('authSessionsLimit', $authValues['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT);
|
||||||
$document->setAttribute('authPasswordHistory', $authValues['passwordHistory'] ?? 0);
|
$document->setAttribute('authPasswordHistory', $authValues['passwordHistory'] ?? 0);
|
||||||
$document->setAttribute('authPasswordDictionary', $authValues['passwordDictionary'] ?? false);
|
$document->setAttribute('authPasswordDictionary', $authValues['passwordDictionary'] ?? false);
|
||||||
|
$document->setAttribute('authPersonalDataCheck', $authValues['personalDataCheck'] ?? false);
|
||||||
|
|
||||||
foreach ($auth as $index => $method) {
|
foreach ($auth as $index => $method) {
|
||||||
$key = $method['key'];
|
$key = $method['key'];
|
||||||
|
|
|
@ -1336,7 +1336,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(409, $response['headers']['status-code']);
|
$this->assertEquals(400, $response['headers']['status-code']);
|
||||||
|
|
||||||
$headers = array_merge($this->getHeaders(), [
|
$headers = array_merge($this->getHeaders(), [
|
||||||
'x-appwrite-mode' => 'admin',
|
'x-appwrite-mode' => 'admin',
|
||||||
|
@ -1348,7 +1348,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(409, $response['headers']['status-code']);
|
$this->assertEquals(400, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1497,6 +1497,126 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateProject
|
||||||
|
*/
|
||||||
|
public function testUpdateDisallowPersonalData($data): void
|
||||||
|
{
|
||||||
|
$id = $data['projectId'] ?? '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable Disallowing of Personal Data
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/auth/personal-data', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'enabled' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals(true, $response['body']['authPersonalDataCheck']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for failure
|
||||||
|
*/
|
||||||
|
$email = uniqid() . 'user@localhost.test';
|
||||||
|
$password = 'password';
|
||||||
|
$name = 'username';
|
||||||
|
$userId = ID::unique();
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
]), [
|
||||||
|
'email' => $email,
|
||||||
|
'password' => $email,
|
||||||
|
'name' => $name,
|
||||||
|
'userId' => $userId
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(400, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals(400, $response['body']['code']);
|
||||||
|
$this->assertEquals(Exception::USER_PASSWORD_PERSONAL_DATA, $response['body']['type']);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
]), [
|
||||||
|
'email' => $email,
|
||||||
|
'password' => $name,
|
||||||
|
'name' => $name,
|
||||||
|
'userId' => $userId
|
||||||
|
]);
|
||||||
|
|
||||||
|
$phone = '+123456789';
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge($this->getHeaders(), [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
'x-appwrite-mode' => 'admin',
|
||||||
|
]), [
|
||||||
|
'email' => $email,
|
||||||
|
'password' => $phone,
|
||||||
|
'name' => $name,
|
||||||
|
'userId' => $userId,
|
||||||
|
'phone' => $phone
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(400, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals(400, $response['body']['code']);
|
||||||
|
$this->assertEquals(Exception::USER_PASSWORD_PERSONAL_DATA, $response['body']['type']);
|
||||||
|
|
||||||
|
/** Test for success */
|
||||||
|
$email = uniqid() . 'user@localhost.test';
|
||||||
|
$password = 'password';
|
||||||
|
$name = 'username';
|
||||||
|
$userId = ID::unique();
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
]), [
|
||||||
|
'email' => $email,
|
||||||
|
'password' => $password,
|
||||||
|
'name' => $name,
|
||||||
|
'userId' => $userId
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
$email = uniqid() . 'user@localhost.test';
|
||||||
|
$userId = ID::unique();
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge($this->getHeaders(), [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
'x-appwrite-mode' => 'admin',
|
||||||
|
]), [
|
||||||
|
'email' => $email,
|
||||||
|
'password' => $password,
|
||||||
|
'name' => $name,
|
||||||
|
'userId' => $userId,
|
||||||
|
'phone' => $phone
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/auth/personal-data', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'enabled' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals(false, $response['body']['authPersonalDataCheck']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testUpdateProjectServiceStatusAdmin(): array
|
public function testUpdateProjectServiceStatusAdmin(): array
|
||||||
{
|
{
|
||||||
|
|
81
tests/unit/Auth/Validator/PersonalDataTest.php
Normal file
81
tests/unit/Auth/Validator/PersonalDataTest.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\Auth\Validator;
|
||||||
|
|
||||||
|
use Appwrite\Auth\Validator\PersonalData;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class PersonalDataTest extends TestCase
|
||||||
|
{
|
||||||
|
protected ?PersonalData $object = null;
|
||||||
|
|
||||||
|
public function testStrict(): void
|
||||||
|
{
|
||||||
|
$this->object = new PersonalData('userId', 'email@example.com', 'name', '+129492323', true);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('userId'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.userId'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('userId.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.userId.something'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('email@example.com'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.email@example.com'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('email@example.com.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.email@example.com.something'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('name'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.name'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('name.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.name.something'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('+129492323'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.+129492323'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('+129492323.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.+129492323.something'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('129492323'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.129492323'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('129492323.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.129492323.something'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('email'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.email'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('email.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.email.something'), false);
|
||||||
|
|
||||||
|
/** Test for success */
|
||||||
|
$this->assertEquals($this->object->isValid('893pu5egerfsv3rgersvd'), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNotStrict(): void
|
||||||
|
{
|
||||||
|
$this->object = new PersonalData('userId', 'email@example.com', 'name', '+129492323', false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('userId'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('USERID'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.USERID'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('USERID.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.USERID.something'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('email@example.com'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('EMAIL@EXAMPLE.COM'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.EMAIL@EXAMPLE.COM'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('EMAIL@EXAMPLE.COM.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.EMAIL@EXAMPLE.COM.something'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('name'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('NAME'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.NAME'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('NAME.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.NAME.something'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('+129492323'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('129492323'), false);
|
||||||
|
|
||||||
|
$this->assertEquals($this->object->isValid('email'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('EMAIL'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.EMAIL'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('EMAIL.something'), false);
|
||||||
|
$this->assertEquals($this->object->isValid('something.EMAIL.something'), false);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue