From bea7cdf9a1b3bbb41784c09f888aad7a74bc9e72 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Jun 2022 16:06:44 +1000 Subject: [PATCH 01/13] Minor fixes for Forte --- app/PaymentDrivers/Forte/CreditCard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/PaymentDrivers/Forte/CreditCard.php b/app/PaymentDrivers/Forte/CreditCard.php index b9882b3a4349..ad599f32f925 100644 --- a/app/PaymentDrivers/Forte/CreditCard.php +++ b/app/PaymentDrivers/Forte/CreditCard.php @@ -90,7 +90,7 @@ class CreditCard $amount_with_fee = $payment_hash->data->total->amount_with_fee; $invoice_totals = $payment_hash->data->total->invoice_totals; $fee_total = 0; - print_r($payment_hash->data->total); + for ($i = ($invoice_totals * 100) ; $i < ($amount_with_fee * 100); $i++) { $calculated_fee = ( 3 * $i) / 100; $calculated_amount_with_fee = round(($i + $calculated_fee) / 100,2); From a462934d3a0ecee6ff6ed70df90e6c8e4b1830e8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Jun 2022 16:40:56 +1000 Subject: [PATCH 02/13] Minor fixes for Purchase orders --- resources/views/portal/ninja2020/purchase_orders/show.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/portal/ninja2020/purchase_orders/show.blade.php b/resources/views/portal/ninja2020/purchase_orders/show.blade.php index bc2a91e97cd5..50f5cc77a4fe 100644 --- a/resources/views/portal/ninja2020/purchase_orders/show.blade.php +++ b/resources/views/portal/ninja2020/purchase_orders/show.blade.php @@ -3,7 +3,7 @@ @push('head') - + @include('portal.ninja2020.components.no-cache') From 61143458d81cad8258eb5b84f83152493929d320 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Jun 2022 20:40:01 +1000 Subject: [PATCH 03/13] Data-signup --- resources/views/index/index.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/index/index.blade.php b/resources/views/index/index.blade.php index a7596262599f..1409f8f6b2f3 100644 --- a/resources/views/index/index.blade.php +++ b/resources/views/index/index.blade.php @@ -1,5 +1,5 @@ - + From 94f224cf2e5b9878f51e9fd38a2f1659b1ed005b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Jun 2022 20:41:18 +1000 Subject: [PATCH 04/13] Reduce scopes for microsoft email --- app/Http/Controllers/Auth/LoginController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 113675dae44e..bd5afef4678f 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -696,7 +696,7 @@ class LoginController extends BaseController } if($provider == 'microsoft'){ - $scopes = ['email', 'Mail.ReadWrite', 'Mail.Send', 'offline_access', 'profile', 'User.Read openid']; + $scopes = ['email', 'Mail.Send', 'offline_access', 'profile', 'User.Read openid']; $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url')."/auth/microsoft"]; } From a170c4a4d17cd5765c221af533af6550f66463b9 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Jun 2022 21:13:24 +1000 Subject: [PATCH 05/13] Permissions updated at --- app/Http/Controllers/UserController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 384064bd594a..3d1bf2e1d44d 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -396,7 +396,7 @@ class UserController extends BaseController UserEmailChanged::dispatch($new_user, json_decode($old_user), auth()->user()->company()); } - $user->company_users()->update(["permissions_updated_at" => now()]); + // $user->company_users()->update(["permissions_updated_at" => now()]); event(new UserWasUpdated($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); From 3fbd1849b3dc30970d3b01b43ebcb73f44ebb509 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Jun 2022 07:47:15 +1000 Subject: [PATCH 06/13] Password protection route with Microsoft OAuth --- app/Http/Middleware/PasswordProtection.php | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index b13c9a1e3a86..8c6b4398f51b 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -61,12 +61,26 @@ class PasswordProtection }elseif( $request->header('X-API-OAUTH-PASSWORD') && strlen($request->header('X-API-OAUTH-PASSWORD')) >=1){ + $user = false; + //user is attempting to reauth with OAuth - check the token value //todo expand this to include all OAuth providers - $user = false; - $google = new Google(); - $user = $google->getTokenResponse(request()->header('X-API-OAUTH-PASSWORD')); - + if(auth()->user()->oauth_provider_id == 'google') + { + $user = false; + $google = new Google(); + $user = $google->getTokenResponse(request()->header('X-API-OAUTH-PASSWORD')); + } + elseif(auth()->user()->oauth_provider_id == 'microsoft') + { + nlog(request()->header('X-API-OAUTH-PASSWORD')); + nlog(auth()->user()->oauth_user_token); + if(request()->header('X-API-OAUTH-PASSWORD') == auth()->user()->oauth_user_token){ + Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); + return $next($request); + } + } + if (is_array($user)) { $query = [ From 263ae4f3ac1c9d8272a3cd82d0c026065c3c74af Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Jun 2022 08:30:06 +1000 Subject: [PATCH 07/13] Password protection route with Microsoft OAuth --- app/Http/Middleware/PasswordProtection.php | 63 ++++++++++++---------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index 8c6b4398f51b..46f2df86eb17 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -61,8 +61,6 @@ class PasswordProtection }elseif( $request->header('X-API-OAUTH-PASSWORD') && strlen($request->header('X-API-OAUTH-PASSWORD')) >=1){ - $user = false; - //user is attempting to reauth with OAuth - check the token value //todo expand this to include all OAuth providers if(auth()->user()->oauth_provider_id == 'google') @@ -70,41 +68,52 @@ class PasswordProtection $user = false; $google = new Google(); $user = $google->getTokenResponse(request()->header('X-API-OAUTH-PASSWORD')); + + if (is_array($user)) { + + $query = [ + 'oauth_user_id' => $google->harvestSubField($user), + 'oauth_provider_id'=> 'google' + ]; + + //If OAuth and user also has a password set - check both + if ($existing_user = MultiDB::hasUser($query) && auth()->user()->company()->oauth_password_required && auth()->user()->has_password && Hash::check(auth()->user()->password, $x_api_password)) { + + nlog("existing user with password"); + + Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); + + return $next($request); + } + elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->company()->oauth_password_required){ + + nlog("existing user without password"); + + Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); + return $next($request); + } + } + } elseif(auth()->user()->oauth_provider_id == 'microsoft') { nlog(request()->header('X-API-OAUTH-PASSWORD')); - nlog(auth()->user()->oauth_user_token); - if(request()->header('X-API-OAUTH-PASSWORD') == auth()->user()->oauth_user_token){ + + $graph = new \Microsoft\Graph\Graph(); + $graph->setAccessToken(request()->header('X-API-OAUTH-PASSWORD')); + + $user = $graph->createRequest("GET", "/me") + ->setReturnType(Model\User::class) + ->execute(); + + if($user && ($user->getId() == auth()->user()->oauth_user_id){ + Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); return $next($request); } } - if (is_array($user)) { - - $query = [ - 'oauth_user_id' => $google->harvestSubField($user), - 'oauth_provider_id'=> 'google' - ]; - //If OAuth and user also has a password set - check both - if ($existing_user = MultiDB::hasUser($query) && auth()->user()->company()->oauth_password_required && auth()->user()->has_password && Hash::check(auth()->user()->password, $x_api_password)) { - - nlog("existing user with password"); - - Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); - - return $next($request); - } - elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->company()->oauth_password_required){ - - nlog("existing user without password"); - - Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); - return $next($request); - } - } return response()->json($error, 412); From e8b3fff483a4c73e94473031cf5addd16f7948b0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Jun 2022 08:31:19 +1000 Subject: [PATCH 08/13] Password protection route with Microsoft OAuth --- app/Http/Middleware/PasswordProtection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index 46f2df86eb17..412a53d29f2d 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -106,7 +106,7 @@ class PasswordProtection ->setReturnType(Model\User::class) ->execute(); - if($user && ($user->getId() == auth()->user()->oauth_user_id){ + if($user && ($user->getId() == auth()->user()->oauth_user_id)){ Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); return $next($request); From 296c6a56a8ff5feeb0f02617e0237c5ca34406b2 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Jun 2022 08:39:45 +1000 Subject: [PATCH 09/13] Password protection route with Microsoft OAuth --- app/Http/Middleware/PasswordProtection.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index 412a53d29f2d..ea702b3f0a65 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -97,16 +97,15 @@ class PasswordProtection } elseif(auth()->user()->oauth_provider_id == 'microsoft') { - nlog(request()->header('X-API-OAUTH-PASSWORD')); + try{ + $payload = json_decode(base64_decode(str_replace('_', '/', str_replace('-','+',explode('.', request()->header('X-API-OAUTH-PASSWORD'))[1])))); + } + catch(\Exception $e){ + nlog("could not decode microsoft response"); + return response()->json(['message' => 'Could not decode the response from Microsoft'], 412); + } - $graph = new \Microsoft\Graph\Graph(); - $graph->setAccessToken(request()->header('X-API-OAUTH-PASSWORD')); - - $user = $graph->createRequest("GET", "/me") - ->setReturnType(Model\User::class) - ->execute(); - - if($user && ($user->getId() == auth()->user()->oauth_user_id)){ + if($payload->preferred_username == auth()->user()->email)){ Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); return $next($request); From 3266af0db5c0661f6774c2ec9b15a545ac2d4f42 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Jun 2022 08:40:29 +1000 Subject: [PATCH 10/13] Password protection route with Microsoft OAuth --- app/Http/Middleware/PasswordProtection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index ea702b3f0a65..456bb3cf9d77 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -105,7 +105,7 @@ class PasswordProtection return response()->json(['message' => 'Could not decode the response from Microsoft'], 412); } - if($payload->preferred_username == auth()->user()->email)){ + if($payload->preferred_username == auth()->user()->email){ Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); return $next($request); From 81a8e67b3557c661fe818f1fbafc8d73ddd6bc57 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Jun 2022 08:46:27 +1000 Subject: [PATCH 11/13] Fixes for connected accounts --- .../ConnectedAccountController.php | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/app/Http/Controllers/ConnectedAccountController.php b/app/Http/Controllers/ConnectedAccountController.php index a520045fdbe3..dd4c3d91b931 100644 --- a/app/Http/Controllers/ConnectedAccountController.php +++ b/app/Http/Controllers/ConnectedAccountController.php @@ -81,12 +81,58 @@ class ConnectedAccountController extends BaseController return $this->handleGoogleOauth(); } + if ($request->input('provider') == 'microsoft') { + return $this->handleMicrosoftOauth($request); + } + return response() ->json(['message' => 'Provider not supported'], 400) ->header('X-App-Version', config('ninja.app_version')) ->header('X-Api-Version', config('ninja.minimum_client_version')); } + private function handleMicrosoftOauth($request) + { + nlog($request->all()); + + $graph = new \Microsoft\Graph\Graph(); + $graph->setAccessToken($request->input('access_token')); + + $user = $graph->createRequest("GET", "/me") + ->setReturnType(Model\User::class) + ->execute(); + + if($user){ + + $email = $user->getMail() ?: $user->getUserPrincipalName(); + + if(auth()->user()->email != $email && MultiDB::checkUserEmailExists($email)) + return response()->json(['message' => ctrans('texts.email_already_register')], 400); + + $connected_account = [ + 'email' => $email, + 'oauth_user_id' => $user->getId(), + 'oauth_provider_id' => 'microsoft', + 'email_verified_at' =>now() + ]; + + auth()->user()->update($connected_account); + auth()->user()->email_verified_at = now(); + auth()->user()->save(); + + $this->setLoginCache(auth()->user()); + + return $this->itemResponse(auth()->user()); + + } + + return response() + ->json(['message' => ctrans('texts.invalid_credentials')], 401) + ->header('X-App-Version', config('ninja.app_version')) + ->header('X-Api-Version', config('ninja.minimum_client_version')); + + } + private function handleGoogleOauth() { $user = false; From a26fd73169bff9ec142bbd2bf58185e6c3c5fa61 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Jun 2022 08:49:12 +1000 Subject: [PATCH 12/13] Fixes for connected accounts --- app/Http/Controllers/ConnectedAccountController.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Http/Controllers/ConnectedAccountController.php b/app/Http/Controllers/ConnectedAccountController.php index dd4c3d91b931..b8544e1a5d96 100644 --- a/app/Http/Controllers/ConnectedAccountController.php +++ b/app/Http/Controllers/ConnectedAccountController.php @@ -95,6 +95,9 @@ class ConnectedAccountController extends BaseController { nlog($request->all()); + if(!$request->has('account_token')) + return response()->json(['message' => 'No access_token parameter found!'], 400); + $graph = new \Microsoft\Graph\Graph(); $graph->setAccessToken($request->input('access_token')); From 77845c22596639f997da89047e3c58a56508b68c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Jun 2022 09:13:56 +1000 Subject: [PATCH 13/13] Always ensure contacts can pay an invoice with an invitation link --- app/Http/Controllers/ClientPortal/InvitationController.php | 3 +++ app/Services/ClientPortal/InstantPayment.php | 1 + 2 files changed, 4 insertions(+) diff --git a/app/Http/Controllers/ClientPortal/InvitationController.php b/app/Http/Controllers/ClientPortal/InvitationController.php index 490a60f31a34..7cd2d273174a 100644 --- a/app/Http/Controllers/ClientPortal/InvitationController.php +++ b/app/Http/Controllers/ClientPortal/InvitationController.php @@ -235,6 +235,9 @@ class InvitationController extends Controller ->with('contact.client') ->firstOrFail(); + if($invitation->contact->trashed()) + $invitation->contact->restore(); + auth()->guard('contact')->loginUsingId($invitation->contact->id, true); $invoice = $invitation->invoice; diff --git a/app/Services/ClientPortal/InstantPayment.php b/app/Services/ClientPortal/InstantPayment.php index ae43fd605ab1..ca6184234fbc 100644 --- a/app/Services/ClientPortal/InstantPayment.php +++ b/app/Services/ClientPortal/InstantPayment.php @@ -255,6 +255,7 @@ class InstantPayment 'tokens' => $tokens, 'payment_method_id' => $payment_method_id, 'amount_with_fee' => $invoice_totals + $fee_totals, + 'client' => $client, ]; if ($is_credit_payment || $totals <= 0) {