From e2ed1fad8b721823adcaf219512ba7654c96b637 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 1 Mar 2020 16:00:54 +1100 Subject: [PATCH] Enable client contact password to be set via API. (#3402) * Tests for client contact passwords * test for client API * Client Tests for password quality * Final tests for client contact password --- .../Requests/Client/StoreClientRequest.php | 26 ++- .../Requests/Client/UpdateClientRequest.php | 30 ++- app/Models/Company.php | 2 +- app/Transformers/CompanyTransformer.php | 1 + tests/Feature/ClientTest.php | 173 ++++++++++++++++++ 5 files changed, 219 insertions(+), 13 deletions(-) diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index 5ca976801d6e..be7ec78824fb 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -42,6 +42,16 @@ class StoreClientRequest extends Request $rules['id_number'] = 'unique:clients,id_number,' . $this->id . ',id,company_id,' . $this->company_id; $rules['settings'] = new ValidClientGroupSettingsRule(); $rules['contacts.*.email'] = 'nullable|distinct'; + $rules['contacts.*.password'] = [ + 'sometimes', + 'string', + 'min:7', // must be at least 10 characters in length + 'regex:/[a-z]/', // must contain at least one lowercase letter + 'regex:/[A-Z]/', // must contain at least one uppercase letter + 'regex:/[0-9]/', // must contain at least one digit + //'regex:/[@$!%*#?&.]/', // must contain a special character + ]; + // $contacts = request('contacts'); @@ -72,13 +82,25 @@ class StoreClientRequest extends Request { foreach($input['contacts'] as $key => $contact) { - if(is_numeric($contact['id'])) + if(array_key_exists('id', $contact) && is_numeric($contact['id'])) unset($input['contacts'][$key]['id']); - elseif(is_string($contact['id'])) + elseif(array_key_exists('id', $contact) && is_string($contact['id'])) $input['contacts'][$key]['id'] = $this->decodePrimaryKey($contact['id']); + + + //Filter the client contact password - if it is sent with ***** we should ignore it! + if(isset($contact['password'])) + { + $contact['password'] = str_replace("*", "", $contact['password']); + + if(strlen($contact['password']) == 0) + unset($input['contacts'][$key]['password']); + } + } } + $this->replace($input); } diff --git a/app/Http/Requests/Client/UpdateClientRequest.php b/app/Http/Requests/Client/UpdateClientRequest.php index 78c942428096..823190550769 100644 --- a/app/Http/Requests/Client/UpdateClientRequest.php +++ b/app/Http/Requests/Client/UpdateClientRequest.php @@ -49,15 +49,16 @@ class UpdateClientRequest extends Request $rules['id_number'] = 'unique:clients,id_number,' . $this->id . ',id,company_id,' . $this->company_id; $rules['settings'] = new ValidClientGroupSettingsRule(); $rules['contacts.*.email'] = 'nullable|distinct'; + $rules['contacts.*.password'] = [ + 'sometimes', + 'string', + 'min:7', // must be at least 10 characters in length + 'regex:/[a-z]/', // must contain at least one lowercase letter + 'regex:/[A-Z]/', // must contain at least one uppercase letter + 'regex:/[0-9]/', // must contain at least one digit + //'regex:/[@$!%*#?&.]/', // must contain a special character + ]; - // $contacts = request('contacts'); - - // if (is_array($contacts)) { - // // for ($i = 0; $i < count($contacts); $i++) { - // // // $rules['contacts.' . $i . '.email'] = 'nullable|email|unique:client_contacts,email,' . isset($contacts[$i]['id'].',company_id,'.$this->company_id); - // // //$rules['contacts.' . $i . '.email'] = 'nullable|email'; - // // } - // } return $rules; } @@ -83,11 +84,20 @@ class UpdateClientRequest extends Request { foreach($input['contacts'] as $key => $contact) { - if(is_numeric($contact['id'])) + if(array_key_exists('id', $contact) && is_numeric($contact['id'])) unset($input['contacts'][$key]['id']); - elseif(is_string($contact['id'])) + elseif(array_key_exists('id', $contact) && is_string($contact['id'])) $input['contacts'][$key]['id'] = $this->decodePrimaryKey($contact['id']); } + + //Filter the client contact password - if it is sent with ***** we should ignore it! + if(isset($contact['password'])) + { + $contact['password'] = str_replace("*", "", $contact['password']); + + if(strlen($contact['password']) == 0) + unset($input['contacts'][$key]['password']); + } } $this->replace($input); diff --git a/app/Models/Company.php b/app/Models/Company.php index 3197636c63e6..7b75de666065 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -325,7 +325,7 @@ class Company extends BaseModel public function domain() { - return 'https://' . $this->subdomain . config('ninja.app_domain'); + return $this->subdomain . config('ninja.app_domain'); } public function notification(Notification $notification) diff --git a/app/Transformers/CompanyTransformer.php b/app/Transformers/CompanyTransformer.php index 01d22efb34f8..96eaa33bf320 100644 --- a/app/Transformers/CompanyTransformer.php +++ b/app/Transformers/CompanyTransformer.php @@ -105,6 +105,7 @@ class CompanyTransformer extends EntityTransformer 'portal_domain' => (string) $company->portal_domain ?: '', 'settings' => $company->settings ?: '', 'enabled_tax_rates' => (int)$company->enabled_tax_rates, + 'enabled_modules' => (int)$company->enabled_modules, 'updated_at' => (int)$company->updated_at, 'archived_at' => (int)$company->deleted_at, 'created_at' =>(int)$company->created_at, diff --git a/tests/Feature/ClientTest.php b/tests/Feature/ClientTest.php index 255a0a1d6400..587700c3116f 100644 --- a/tests/Feature/ClientTest.php +++ b/tests/Feature/ClientTest.php @@ -7,6 +7,7 @@ use App\Models\Account; use App\Models\Client; use App\Models\ClientContact; use App\Models\Company; +use App\Models\CompanyToken; use App\Models\User; use App\Utils\Traits\MakesHash; use Faker\Factory; @@ -17,6 +18,7 @@ use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Session; +use Illuminate\Validation\ValidationException; use Tests\TestCase; /** @@ -38,6 +40,9 @@ class ClientTest extends TestCase Model::reguard(); + $this->withoutExceptionHandling(); + + } public function testClientList() @@ -266,6 +271,174 @@ class ClientTest extends TestCase $this->assertEquals($client->contacts->count(), 3); } + + public function testCreatingClientAndContacts() + { + + $account = factory(\App\Models\Account::class)->create(); + $company = factory(\App\Models\Company::class)->create([ + 'account_id' => $account->id, + ]); + + $account->default_company_id = $company->id; + $account->save(); + + $user = factory(\App\Models\User::class)->create([ + // 'account_id' => $account->id, + 'confirmation_code' => $this->createDbHash(config('database.default')) + ]); + + $user->companies()->attach($company->id, [ + 'account_id' => $account->id, + 'is_owner' => 1, + 'is_admin' => 1, + 'permissions' => '', + 'settings' => '', + 'is_locked' => 0, + ]); + + $ct = CompanyToken::create([ + 'account_id' => $account->id, + 'company_id' => $company->id, + 'user_id' => $user->id, + 'token' => \Illuminate\Support\Str::random(64), + 'name' => $user->first_name. ' '. $user->last_name, + ]); + + $token = $ct->token; + + $data = [ + 'name' => 'A loyal Client', + 'contacts' => [ + ['email' => $this->faker->unique()->safeEmail] + ] + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->post('/api/v1/clients/', $data) + ->assertStatus(200); + + // $arr = $response->json(); + + $data = [ + 'name' => 'A loyal Client', + 'contacts' => [ + [ + 'email' => $this->faker->unique()->safeEmail, + 'password' => '*****', + ] + ] + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->post('/api/v1/clients/', $data) + ->assertStatus(200); + + + $data = [ + 'name' => 'A loyal Client', + 'contacts' => [ + [ + 'email' => $this->faker->unique()->safeEmail, + 'password' => '1' + ] + ] + ]; + + $response = null; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->post('/api/v1/clients/', $data); + } + catch(ValidationException $e) { + + $message = json_decode($e->validator->getMessageBag(),1); + \Log::error($message); + $this->assertNotNull($message); + } + + $data = [ + 'name' => 'A loyal Client', + 'contacts' => [ + [ + 'email' => $this->faker->unique()->safeEmail, + 'password' => '1Qajsj...33' + ], + ] + ]; + + $response = null; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->post('/api/v1/clients/', $data); + } + catch(ValidationException $e) { + + $message = json_decode($e->validator->getMessageBag(),1); + //\Log::error($message); + //$this->assertNotNull($message); + } + + $response->assertStatus(200); + + $data = [ + 'name' => 'A loyal Client', + 'contacts' => [ + [ + 'email' => $this->faker->unique()->safeEmail, + 'password' => '1Qajsj...33' + ], + [ + 'email' => $this->faker->unique()->safeEmail, + 'password' => '1234AAAAAaaaaa' + ], + ] + ]; + + $response = null; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->post('/api/v1/clients/', $data); + } + catch(ValidationException $e) { + + $message = json_decode($e->validator->getMessageBag(),1); + \Log::error($message); + $this->assertNotNull($message); + } + + $response->assertStatus(200); + + + $arr = $response->json(); + + $client_id = $arr['data']['id']; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->put('/api/v1/clients/' . $client_id, $data)->assertStatus(200); + + $arr = $response->json(); + + \Log::error($arr); + + } /** @test */ // public function testMassivelyCreatingClients() // {