From 8fd79ce23ef3885c2bf622769ee531afe455e4d9 Mon Sep 17 00:00:00 2001 From: Deon George Date: Fri, 12 Aug 2022 14:53:06 +1000 Subject: [PATCH] Initial integration with Quicken (Intuit API), rework oauth tables, update/test google login --- .../Auth/SocialLoginController.php | 53 +++++++-- app/Mail/SocialLink.php | 16 +-- app/Models/AccountOauth.php | 46 -------- app/Models/Oauth.php | 24 ---- app/Models/ProviderOauth.php | 24 ++++ app/Models/ProviderToken.php | 17 +++ app/Models/UserOauth.php | 31 +++++ app/Providers/AppServiceProvider.php | 3 +- app/Providers/AuthServiceProvider.php | 4 + composer.json | 5 + composer.lock | 52 ++++++-- config/session.php | 2 +- .../2022_08_11_101411_accounting.php | 111 ++++++++++++++++++ .../theme/backend/adminlte/home.blade.php | 2 +- .../layouts/partials/sidebarmenu.blade.php | 6 + routes/web.php | 2 + 16 files changed, 299 insertions(+), 99 deletions(-) delete mode 100644 app/Models/AccountOauth.php delete mode 100644 app/Models/Oauth.php create mode 100644 app/Models/ProviderOauth.php create mode 100644 app/Models/ProviderToken.php create mode 100644 app/Models/UserOauth.php create mode 100644 database/migrations/2022_08_11_101411_accounting.php diff --git a/app/Http/Controllers/Auth/SocialLoginController.php b/app/Http/Controllers/Auth/SocialLoginController.php index d4252fd..7119354 100644 --- a/app/Http/Controllers/Auth/SocialLoginController.php +++ b/app/Http/Controllers/Auth/SocialLoginController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Auth; +use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Auth; @@ -10,9 +11,7 @@ use Laravel\Socialite\Facades\Socialite; use App\Http\Controllers\Controller; use App\Mail\SocialLink; -use App\Models\Oauth; -use App\Models\AccountOauth; -use App\Models\User; +use App\Models\{ProviderOauth,ProviderToken,User,UserOauth}; use App\Providers\RouteServiceProvider; class SocialLoginController extends Controller @@ -26,10 +25,13 @@ class SocialLoginController extends Controller { $openiduser = Socialite::with($provider)->user(); - $oo = Oauth::firstOrCreate(['name'=>$provider,'active'=>TRUE]); + if (! $openiduser) + return redirect('/home')->with('error','No user details obtained.'); + + $oo = ProviderOauth::firstOrCreate(['name'=>$provider,'active'=>TRUE]); // See if this user has connected and linked previously - $aoo = $oo->accounts->where('userid',$openiduser->id); + $aoo = $oo->users->where('userid',$openiduser->id); if ($aoo->count() == 1) { $aoo = $aoo->first(); @@ -59,10 +61,10 @@ class SocialLoginController extends Controller // See if their is an account with this email address if ($uo->count() == 1) { - $aoo = new AccountOauth; + $aoo = new UserOauth; $aoo->userid = $openiduser->id; $aoo->oauth_data = $openiduser->user; - $oo->accounts()->save($aoo); + $oo->users()->save($aoo); return $this->link($provider,$aoo,$uo->first()); @@ -78,16 +80,43 @@ class SocialLoginController extends Controller return redirect()->intended(RouteServiceProvider::HOME); } + public function handleBearerTokenCallback($provider) + { + $openiduser = Socialite::with($provider)->user(); + + if (! $openiduser) + return redirect('/home')->with('error','No user details obtained.'); + + $po = ProviderOauth::where('name',$provider)->singleOrFail(); + + $uoo = ProviderToken::where('user_id',Auth::id())->where('provider_oauth_id',$po->id)->firstOrNew(); + + $uoo->user_id = Auth::id(); + $uoo->access_token = $openiduser->token; + $uoo->access_token_expires_at = Carbon::now()->addSeconds($openiduser->expiresIn); + $uoo->refresh_token = $openiduser->refreshToken; + $uoo->refresh_token_expires_at = Carbon::now()->addSeconds($openiduser->refresh_token_expires_in); + $uoo->realm_id = $openiduser->realmid; + + $po->tokens()->save($uoo); + + return redirect() + ->intended(RouteServiceProvider::HOME) + ->with('success','Token refreshed.'); + } + /** * We have identified the user and oauth, just need them to confirm the link * - * @param $provider + * @param $provider + * @param UserOauth $ao * @param User $uo - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @return \Illuminate\View\View */ - public function link($provider,AccountOauth $ao,User $uo) + public function link($provider,UserOauth $ao,User $uo): \Illuminate\View\View { - Mail::to($uo->email)->send(new SocialLink($ao)); + // @note If this is sent now (send()), it results in the caller to be executed a second time (handleProviderCallback()). + Mail::to($uo->email)->queue(new SocialLink($ao)); return view('auth.social_link') ->with('oauthid',$ao->id) @@ -97,7 +126,7 @@ class SocialLoginController extends Controller public function linkcomplete(Request $request,$provider) { // Load our oauth id - $aoo = AccountOauth::findOrFail($request->post('oauthid')); + $aoo = UserOauth::findOrFail($request->post('oauthid')); // Check our email matches if (Arr::get($aoo->oauth_data,'email','invalid') !== $request->post('email')) diff --git a/app/Mail/SocialLink.php b/app/Mail/SocialLink.php index 8b3aada..9fb50ab 100644 --- a/app/Mail/SocialLink.php +++ b/app/Mail/SocialLink.php @@ -6,24 +6,26 @@ use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; -use App\Models\{AccountOauth,User}; +use App\Models\{Site,User,UserOauth}; class SocialLink extends Mailable { use Queueable, SerializesModels; public string $token; - public User $user; + public Site $site; + public ?User $user; /** * Create a new message instance. * - * @param AccountOauth $o + * @param UserOauth $o */ - public function __construct(AccountOauth $o) + public function __construct(UserOauth $o) { + $this->site = $o->site; $this->token = $o->link_token; - $this->user = $o->account->user; + $this->user = $o->user; } /** @@ -37,7 +39,7 @@ class SocialLink extends Mailable ->markdown('email.system.social_link') ->subject('Link your Account') ->with([ - 'site'=>$this->user->site, - ]); + 'site'=>$this->site, + ]); } } \ No newline at end of file diff --git a/app/Models/AccountOauth.php b/app/Models/AccountOauth.php deleted file mode 100644 index 88d699b..0000000 --- a/app/Models/AccountOauth.php +++ /dev/null @@ -1,46 +0,0 @@ -'array', - ]; - - public function account() - { - return $this->belongsTo(Account::class); - } - - public function site() - { - return $this->belongsTo(Site::class); - } - - public function User() - { - return $this->belongsTo(User::class); - } - - /** - * Get a link token to use when validating account. - */ - public function getLinkTokenAttribute(): string - { - return strtoupper(substr(md5($this->id.$this->date_last),0,8)); - } -} \ No newline at end of file diff --git a/app/Models/Oauth.php b/app/Models/Oauth.php deleted file mode 100644 index e5ec652..0000000 --- a/app/Models/Oauth.php +++ /dev/null @@ -1,24 +0,0 @@ -hasMany(AccountOauth::class); - } -} \ No newline at end of file diff --git a/app/Models/ProviderOauth.php b/app/Models/ProviderOauth.php new file mode 100644 index 0000000..c0bbc19 --- /dev/null +++ b/app/Models/ProviderOauth.php @@ -0,0 +1,24 @@ +hasMany(ProviderToken::class); + } + + public function users() + { + return $this->hasMany(UserOauth::class); + } +} \ No newline at end of file diff --git a/app/Models/ProviderToken.php b/app/Models/ProviderToken.php new file mode 100644 index 0000000..b3b22b5 --- /dev/null +++ b/app/Models/ProviderToken.php @@ -0,0 +1,17 @@ +'json', + ]; + + public function User() + { + return $this->belongsTo(User::class); + } + + /** + * Get a link token to use when validating account. + */ + public function getLinkTokenAttribute(): string + { + return strtoupper(substr(md5($this->id.$this->date_last),0,8)); + } +} \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index b4ff86e..affc9ae 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; +use Laravel\Passport\Passport; use Leenooks\Traits\SingleOrFail; class AppServiceProvider extends ServiceProvider @@ -16,7 +17,7 @@ class AppServiceProvider extends ServiceProvider */ public function register() { - // + Passport::ignoreMigrations(); } /** diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index e72cd67..08ff2ed 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,12 +2,15 @@ namespace App\Providers; +use Intuit\Traits\IntuitSocialite; use Laravel\Passport\Passport; use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { + use IntuitSocialite; + /** * The policy mappings for the application. * @@ -25,6 +28,7 @@ class AuthServiceProvider extends ServiceProvider public function boot() { $this->registerPolicies(); + $this->bootIntuitSocialite(); Passport::routes(); // Passport::enableImplicitGrant(); diff --git a/composer.json b/composer.json index d8a0934..2cf3624 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "laravel/socialite": "^5.2", "laravel/ui": "^3.2", "leenooks/dreamscape": "^0.1.0", + "leenooks/intuit": "^0.1.0", "leenooks/laravel": "^9.2.3", "leenooks/laravel-theme": "^v2.0.18", "nunomaduro/laravel-console-summary": "^1.8", @@ -50,6 +51,10 @@ } }, "repositories": { + "intuit": { + "type": "vcs", + "url": "https://dev.leenooks.net/leenooks/intuit" + }, "laravel": { "type": "vcs", "url": "https://dev.leenooks.net/leenooks/laravel" diff --git a/composer.lock b/composer.lock index d27f5b8..c34d097 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d3d12d0317a55fce7dedf86c4c567aba", + "content-hash": "d82522975cd8f5a5e902d49a94527919", "packages": [ { "name": "asm89/stack-cors", @@ -142,16 +142,16 @@ }, { "name": "brick/math", - "version": "0.10.1", + "version": "0.10.2", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "de846578401f4e58f911b3afeb62ced56365ed87" + "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/de846578401f4e58f911b3afeb62ced56365ed87", - "reference": "de846578401f4e58f911b3afeb62ced56365ed87", + "url": "https://api.github.com/repos/brick/math/zipball/459f2781e1a08d52ee56b0b1444086e038561e3f", + "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f", "shasum": "" }, "require": { @@ -186,7 +186,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.10.1" + "source": "https://github.com/brick/math/tree/0.10.2" }, "funding": [ { @@ -194,7 +194,7 @@ "type": "github" } ], - "time": "2022-08-01T22:54:31+00:00" + "time": "2022-08-10T22:54:19+00:00" }, { "name": "clarkeash/doorman", @@ -3371,6 +3371,44 @@ ], "time": "2022-08-10T06:07:35+00:00" }, + { + "name": "leenooks/intuit", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://dev.leenooks.net/leenooks/intuit", + "reference": "bbbdff040fa7d49e5acec38ce3a7c5a35af58a30" + }, + "require": { + "jenssegers/model": "^1.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Intuit\\Providers\\IntuitServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Intuit\\": "src" + } + }, + "authors": [ + { + "name": "Deon George", + "email": "deon@leenooks.net" + } + ], + "description": "Intuit API", + "keywords": [ + "intuit", + "laravel", + "leenooks" + ], + "time": "2022-08-12T04:41:22+00:00" + }, { "name": "leenooks/laravel", "version": "9.2.5", diff --git a/config/session.php b/config/session.php index d699a5c..d2accdc 100644 --- a/config/session.php +++ b/config/session.php @@ -194,6 +194,6 @@ return [ | */ - 'same_site' => 'strict', + 'same_site' => 'lax', ]; diff --git a/database/migrations/2022_08_11_101411_accounting.php b/database/migrations/2022_08_11_101411_accounting.php new file mode 100644 index 0000000..fdb1836 --- /dev/null +++ b/database/migrations/2022_08_11_101411_accounting.php @@ -0,0 +1,111 @@ +dropForeign('ab_oauth_site_id_foreign'); + $table->dropIndex('ab_oauth_id_site_id_index'); + $table->dropIndex('ab_oauth_site_id_foreign'); + + $table->foreign(['site_id'])->references(['id'])->on('sites'); + }); + + DB::statement('RENAME TABLE quickbooks_tokens TO provider_tokens'); + DB::statement('ALTER TABLE provider_tokens MODIFY realm_id TEXT DEFAULT NULL,MODIFY access_token TEXT NOT NULL,MODIFY refresh_token TEXT DEFAULT NULL'); + DB::statement('ALTER TABLE provider_tokens MODIFY access_token_expires_at datetime DEFAULT NULL,MODIFY refresh_token_expires_at datetime DEFAULT NULL'); + + Schema::table('provider_tokens', function (Blueprint $table) { + $table->integer('provider_oauth_id')->unsigned(); + $table->integer('site_id')->unsigned(); + + $table->unique(['provider_oauth_id','user_id']); + $table->foreign(['user_id','site_id'])->references(['id','site_id'])->on('users'); + $table->foreign(['provider_oauth_id','site_id'])->references(['id','site_id'])->on('provider_oauth'); + }); + + // delete from ab_account_oauth where account_id=144; + // update ab_account_oauth set user_id =1 where account_id=1; + // update ab_account_oauth set user_id =109 where id=12; + DB::statement('RENAME TABLE ab_account_oauth TO user_oauth'); + DB::statement('ALTER TABLE user_oauth RENAME COLUMN oauth_id TO provider_oauth_id'); + Schema::table('user_oauth', function (Blueprint $table) { + $table->datetime('created_at')->nullable()->after('id'); + $table->datetime('updated_at')->nullable()->after('created_at'); + }); + + foreach (\App\Models\UserOauth::withoutGlobalScope(\App\Models\Scopes\SiteScope::class)->cursor() as $o) { + if ($o->date_orig) + $o->created_at = \Carbon\Carbon::createFromTimestamp($o->date_orig); + if ($o->date_last) + $o->updated_at = \Carbon\Carbon::createFromTimestamp($o->date_last); + + if ($o->getRawOriginal('oauth_data') && ! is_array($o->oauth_data)) { + try { + $o->oauth_data = unserialize(gzuncompress($o->getRawOriginal('oauth_data'))); + } catch (Exception $e) { + } + } + + $o->save(); + } + + DB::statement('ALTER TABLE user_oauth MODIFY provider_oauth_id int unsigned NOT NULL'); + Schema::table('user_oauth', function (Blueprint $table) { + $table->dropColumn(['date_orig','date_last','account_id']); + $table->dropForeign('ab_account_oauth_site_id_foreign'); + $table->dropIndex('ab_account_oauth_site_id_foreign'); + $table->dropIndex('ab_account_oauth_id_site_id_index'); + $table->foreign(['user_id','site_id'])->references(['id','site_id'])->on('users'); + $table->foreign(['provider_oauth_id','site_id'])->references(['id','site_id'])->on('provider_oauth'); + }); + + // Fix incorrect site references + Schema::table('accounts', function (Blueprint $table) { + $table->dropForeign(['site_id']); + }); + Schema::table('services', function (Blueprint $table) { + $table->dropForeign(['site_id']); + }); + Schema::table('costs', function (Blueprint $table) { + $table->dropForeign(['site_id']); + $table->foreign(['site_id'])->references(['id'])->on('sites'); + }); + Schema::table('products', function (Blueprint $table) { + $table->dropForeign(['site_id']); + $table->foreign(['site_id'])->references(['id'])->on('sites'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + abort(500,'cant go back'); + } +}; diff --git a/resources/views/theme/backend/adminlte/home.blade.php b/resources/views/theme/backend/adminlte/home.blade.php index f34486a..ffe355d 100644 --- a/resources/views/theme/backend/adminlte/home.blade.php +++ b/resources/views/theme/backend/adminlte/home.blade.php @@ -11,7 +11,7 @@ {{ $o->full_name }} @endsection @section('contentheader_description') - {{ $o->role }} + {{ $o->role }} @endsection @section('main-content') diff --git a/resources/views/vendor/adminlte/layouts/partials/sidebarmenu.blade.php b/resources/views/vendor/adminlte/layouts/partials/sidebarmenu.blade.php index 6ceebb2..13d0ecb 100644 --- a/resources/views/vendor/adminlte/layouts/partials/sidebarmenu.blade.php +++ b/resources/views/vendor/adminlte/layouts/partials/sidebarmenu.blade.php @@ -71,6 +71,12 @@ @can('wholesaler') + +