From a6282cee56af32a314217bf50bc3098d2178fa1c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 4 Mar 2021 08:30:11 +1100 Subject: [PATCH 1/5] Create single account from command line --- app/Console/Commands/CreateAccount.php | 163 +++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 app/Console/Commands/CreateAccount.php diff --git a/app/Console/Commands/CreateAccount.php b/app/Console/Commands/CreateAccount.php new file mode 100644 index 000000000000..053b7661400b --- /dev/null +++ b/app/Console/Commands/CreateAccount.php @@ -0,0 +1,163 @@ +info(date('r').' Create Single Account...'); + + $this->warmCache(); + + $this->createAccount(); + + } + + private function createAccount() + { + + $account = Account::factory()->create(); + $company = Company::factory()->create([ + 'account_id' => $account->id, + ]); + + $account->default_company_id = $company->id; + $account->save(); + + $email = $this->option('email') ?? 'admin@example.com'; + $password = $this->option('password') ?? 'changeme!'; + + $user = User::factory()->create([ + 'account_id' => $account->id, + 'email' => $email, + 'password' => Hash::make($password), + 'confirmation_code' => $this->createDbHash(config('database.default')), + 'email_verified_at' => now(), + ]); + + $company_token = new CompanyToken; + $company_token->user_id = $user->id; + $company_token->company_id = $company->id; + $company_token->account_id = $account->id; + $company_token->name = 'User Token'; + $company_token->token = Str::random(64); + $company_token->is_system = true; + + $company_token->save(); + + $user->companies()->attach($company->id, [ + 'account_id' => $account->id, + 'is_owner' => 1, + 'is_admin' => 1, + 'is_locked' => 0, + 'notifications' => CompanySettings::notificationDefaults(), + 'settings' => null, + ]); + + CreateCompanyPaymentTerms::dispatchNow($company, $user); + CreateCompanyTaskStatuses::dispatchNow($company, $user); + VersionCheck::dispatchNow(); + + } + + private function warmCache() + { + /* Warm up the cache !*/ + $cached_tables = config('ninja.cached_tables'); + + foreach ($cached_tables as $name => $class) { + if (! Cache::has($name)) { + // check that the table exists in case the migration is pending + if (! Schema::hasTable((new $class())->getTable())) { + continue; + } + if ($name == 'payment_terms') { + $orderBy = 'num_days'; + } elseif ($name == 'fonts') { + $orderBy = 'sort_order'; + } elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) { + $orderBy = 'name'; + } else { + $orderBy = 'id'; + } + $tableData = $class::orderBy($orderBy)->get(); + if ($tableData->count()) { + Cache::forever($name, $tableData); + } + } + } + } + +} From 1780db52bd7463cacff9b54929ce7f652de0147b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 4 Mar 2021 09:39:24 +1100 Subject: [PATCH 2/5] Invite user route --- app/Http/Controllers/UserController.php | 16 ++---- .../SendVerificationNotification.php | 18 +------ app/Models/User.php | 6 +++ app/Services/User/UserService.php | 52 +++++++++++++++++++ routes/api.php | 2 +- 5 files changed, 65 insertions(+), 29 deletions(-) create mode 100644 app/Services/User/UserService.php diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 5a00afa0ce4b..ff320634ea20 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -623,8 +623,8 @@ class UserController extends BaseController * Detach an existing user to a company. * * @OA\Post( - * path="/api/v1/users/{user}/reconfirm", - * operationId="reconfirmUser", + * path="/api/v1/users/{user}/invite", + * operationId="inviteUser", * tags={"users"}, * summary="Reconfirm an existing user to a company", * description="Reconfirm an existing user from a company", @@ -666,18 +666,10 @@ class UserController extends BaseController * @param User $user * @return \Illuminate\Http\JsonResponse */ - public function reconfirm(ReconfirmUserRequest $request, User $user) + public function invite(ReconfirmUserRequest $request, User $user) { - $user->confirmation_code = $this->createDbHash($user->company()->db); - $user->save(); - $nmo = new NinjaMailerObject; - $nmo->mailable = new NinjaMailer((new VerifyUserObject($user, $user->company()))->build()); - $nmo->company = $user->company(); - $nmo->to_user = $user; - $nmo->settings = $user->company->settings; - - NinjaMailerJob::dispatch($nmo); + $user->service()->invite($user->company()); return response()->json(['message' => ctrans('texts.confirmation_resent')], 200); diff --git a/app/Listeners/SendVerificationNotification.php b/app/Listeners/SendVerificationNotification.php index 1c396454dd1c..a8d4836bbfcf 100644 --- a/app/Listeners/SendVerificationNotification.php +++ b/app/Listeners/SendVerificationNotification.php @@ -46,24 +46,10 @@ class SendVerificationNotification implements ShouldQueue */ public function handle($event) { + MultiDB::setDB($event->company->db); - try { + $event->user->service()->invite($event->company); - $nmo = new NinjaMailerObject; - $nmo->mailable = new NinjaMailer((new VerifyUserObject($event->user, $event->company))->build()); - $nmo->company = $event->company; - $nmo->to_user = $event->user; - $nmo->settings = $event->company->settings; - - NinjaMailerJob::dispatch($nmo); - - // $event->user->notify(new VerifyUser($event->user, $event->company)); - - Ninja::registerNinjaUser($event->user); - - } catch (Exception $e) { - nlog("I couldn't send the email " . $e->getMessage()); - } } } diff --git a/app/Models/User.php b/app/Models/User.php index 52582d0a95ee..bc9a363abbee 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -17,6 +17,7 @@ use App\Jobs\Mail\NinjaMailerObject; use App\Mail\Admin\ResetPasswordObject; use App\Models\Presenters\UserPresenter; use App\Notifications\ResetPasswordNotification; +use App\Services\User\UserService; use App\Utils\Traits\MakesHash; use App\Utils\Traits\UserSessionAttributes; use App\Utils\Traits\UserSettings; @@ -398,4 +399,9 @@ class User extends Authenticatable implements MustVerifyEmail //$this->notify(new ResetPasswordNotification($token)); } + + public function service() :User + { + return new UserService($this); + } } diff --git a/app/Services/User/UserService.php b/app/Services/User/UserService.php new file mode 100644 index 000000000000..25622af04f10 --- /dev/null +++ b/app/Services/User/UserService.php @@ -0,0 +1,52 @@ +user = $user; + } + + public function invite($company) + { + try { + + $nmo = new NinjaMailerO5bject; + $nmo->mailable = new NinjaMailer((new VerifyUserObject($this->user, $company))->build()); + $nmo->company = $company; + $nmo->to_user = $this->user; + $nmo->settings = $company->settings; + + NinjaMailerJob::dispatch($nmo); + + Ninja::registerNinjaUser($this->user); + + } catch (\Exception $e) { + nlog("I couldn't send the verification email " . $e->getMessage()); + } + + return $this->user; + } +} diff --git a/routes/api.php b/routes/api.php index 244683169dfa..45d6d5683b4e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -164,7 +164,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::delete('users/{user}/detach_from_company', 'UserController@detach')->middleware('password_protected'); Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected'); - Route::post('/user/{user}/reconfirm', 'UserController@reconfirm')->middleware('password_protected'); + Route::post('/users/{user}/invite', 'UserController@invite')->middleware('password_protected'); Route::resource('webhooks', 'WebhookController'); Route::post('webhooks/bulk', 'WebhookController@bulk')->name('webhooks.bulk'); From 365c190cca4c5ce9a4e35745d4f3515673ac791e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 4 Mar 2021 10:12:34 +1100 Subject: [PATCH 3/5] Remove return type for User Service --- app/Http/Middleware/PasswordProtection.php | 2 +- app/Models/User.php | 2 +- app/Transformers/UserTransformer.php | 1 + ...1_add_has_password_field_to_user_table.php | 30 +++++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 database/migrations/2021_03_03_230941_add_has_password_field_to_user_table.php diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index 1df11b733400..16bb85d9921b 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -36,7 +36,7 @@ class PasswordProtection 'errors' => new stdClass, ]; - if($request->header('X-API-OAUTH-PASSWORD')){ + if( $request->header('X-API-OAUTH-PASSWORD') && strlen($request->header('X-API-OAUTH-PASSWORD')) >=1 ){ //user is attempting to reauth with OAuth - check the token value //todo expand this to include all OAuth providers diff --git a/app/Models/User.php b/app/Models/User.php index bc9a363abbee..f9fb94e5b59f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -400,7 +400,7 @@ class User extends Authenticatable implements MustVerifyEmail //$this->notify(new ResetPasswordNotification($token)); } - public function service() :User + public function service() { return new UserService($this); } diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/UserTransformer.php index bd3632ab69b7..6c550feff39a 100644 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/UserTransformer.php @@ -60,6 +60,7 @@ class UserTransformer extends EntityTransformer 'oauth_provider_id' => (string) $user->oauth_provider_id, 'last_confirmed_email_address' => (string) $user->last_confirmed_email_address ?: '', 'google_2fa_secret' => (bool) $user->google_2fa_secret, + 'has_password' => (bool) $user->has_password, ]; } diff --git a/database/migrations/2021_03_03_230941_add_has_password_field_to_user_table.php b/database/migrations/2021_03_03_230941_add_has_password_field_to_user_table.php new file mode 100644 index 000000000000..92f3821c8d6a --- /dev/null +++ b/database/migrations/2021_03_03_230941_add_has_password_field_to_user_table.php @@ -0,0 +1,30 @@ +boolean('has_password')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +} From 07aca3d8c0d6d7c1870628c3f7e5303761d08ae7 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 4 Mar 2021 10:34:18 +1100 Subject: [PATCH 4/5] Fixes for tests --- app/Services/User/UserService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/User/UserService.php b/app/Services/User/UserService.php index 25622af04f10..a085574ff18f 100644 --- a/app/Services/User/UserService.php +++ b/app/Services/User/UserService.php @@ -33,7 +33,7 @@ class UserService { try { - $nmo = new NinjaMailerO5bject; + $nmo = new NinjaMailerObject; $nmo->mailable = new NinjaMailer((new VerifyUserObject($this->user, $company))->build()); $nmo->company = $company; $nmo->to_user = $this->user; From 11ad2cd57eae0e9050bafeb7754fabcfe3e8d4cb Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 4 Mar 2021 12:17:29 +1100 Subject: [PATCH 5/5] Fixes for caching protection route --- app/Http/Middleware/PasswordProtection.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index 16bb85d9921b..20535de73d7b 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -53,7 +53,7 @@ class PasswordProtection /* Cannot allow duplicates! */ if ($existing_user = MultiDB::hasUser($query)) { - Cache::add(auth()->user()->email.'_logged_in', Str::random(64), now()->addMinutes(30)); + Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), now()->addMinutes(30)); return $next($request); } } @@ -74,10 +74,10 @@ class PasswordProtection return response()->json($error, 403); } - } elseif (Cache::get(auth()->user()->email.'_logged_in')) { + } elseif (Cache::get(auth()->user()->hashed_id.'_logged_in')) { - Cache::pull(auth()->user()->email.'_logged_in'); - Cache::add(auth()->user()->email.'_logged_in', Str::random(64), now()->addMinutes(30)); + Cache::pull(auth()->user()->hashed_id.'_logged_in'); + Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), now()->addMinutes(30)); return $next($request);