diff --git a/app/Export/CSV/InvoiceItemExport.php b/app/Export/CSV/InvoiceItemExport.php index 02135e6e12..b3222582d5 100644 --- a/app/Export/CSV/InvoiceItemExport.php +++ b/app/Export/CSV/InvoiceItemExport.php @@ -172,8 +172,7 @@ class InvoiceItemExport extends BaseExport $tmp_key = str_replace("item.", "", $key); if ($tmp_key == 'tax_id') { - // $tmp_key = 'tax_category'; - $item_array[$key] = $this->getTaxCategoryName((int)$item->tax_id); + $item_array[$key] = $this->getTaxCategoryName((int)$item->tax_id ?? 1); // @phpstan-ignore-line } elseif (property_exists($item, $tmp_key)) { $item_array[$key] = $item->{$tmp_key}; diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index a220c49054..1471f77d3b 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -104,12 +104,12 @@ class AccountController extends BaseController $cu = CompanyUser::query()->where('user_id', $account->users()->first()->id); $company_user = $cu->first(); - - $truth = app()->make(TruthSource::class); - $truth->setCompanyUser($company_user); - $truth->setUser($company_user->user); - $truth->setCompany($company_user->company); - $truth->setCompanyToken($company_user->tokens()->where('user_id', $company_user->user_id)->where('company_id', $company_user->company_id)->first()); + + // $truth = app()->make(TruthSource::class); + // $truth->setCompanyUser($company_user); + // $truth->setUser($company_user->user); + // $truth->setCompany($company_user->company); + // $truth->setCompanyToken($company_user->tokens()->where('user_id', $company_user->user_id)->where('company_id', $company_user->company_id)->first()); return $this->listResponse($cu); } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 473befb5d4..9e21cd35af 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -101,7 +101,9 @@ class LoginController extends BaseController if ($user && \Illuminate\Support\Facades\Hash::check(trim($request->password), $user->password)) { //Authenticate for this request only. - Auth::login($user, false); + + auth()->login($user, false); + auth()->user()->setContext($user->account->default_company, $user->tokens()->where('company_id', $user->account->default_company_id)->where('is_system', true)->first()); LightLogs::create(new LoginSuccess()) ->increment() @@ -136,7 +138,7 @@ class LoginController extends BaseController } /** @var \App\Models\CompanyUser $cu */ - $cu = $this->hydrateCompanyUser($user); + $cu = $this->hydrateCompanyUser(); nlog("LOGIN:: ".$request->email." {$user->account_id}"); @@ -177,13 +179,7 @@ class LoginController extends BaseController */ public function refresh(Request $request) { - $truth = app()->make(TruthSource::class); - - // if ($truth->getCompanyToken()) { - // $company_token = $truth->getCompanyToken(); - // } else { - $company_token = CompanyToken::where('token', $request->header('X-API-TOKEN'))->first(); - // } + $company_token = auth()->user()->getCurrentToken(); $cu = CompanyUser::query() ->where('user_id', $company_token->user_id); @@ -295,10 +291,11 @@ class LoginController extends BaseController return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); } - Auth::login($existing_user, false); + auth()->login($existing_user, false); + auth()->user()->setContext($existing_user->account->default_company, $existing_user->tokens()->where('company_id', $existing_user->account->default_company_id)->where('is_system', true)->first()); /** @var \App\Models\CompanyUser $cu */ - $cu = $this->hydrateCompanyUser($existing_user); + $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); @@ -310,39 +307,7 @@ class LoginController extends BaseController return $this->timeConstrainedResponse($cu); } - //If this is a result user/email combo - lets add their OAuth details details - // if ($existing_login_user = MultiDB::hasUser(['email' => $user->email])) { - // if (!$existing_login_user->account) { - // return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); - // } - - // Auth::login($existing_login_user, true); - // /** @var \App\Models\User $user */ - - // $user = auth()->user(); - - // $user->update([ - // 'oauth_user_id' => $user->id, - // 'oauth_provider_id' => $provider, - // ]); - - // /** @var \App\Models\CompanyUser $cu */ - // $cu = $this->hydrateCompanyUser(); - - // if ($cu->count() == 0) { - // return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); - // } - - // if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterprisePaidClient()) { - // return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); - // } - - // return $this->timeConstrainedResponse($cu); - // } - - // nlog("socialite"); - // nlog($user); - + $name = OAuth::splitName($user->name); if ($provider == 'apple') { @@ -371,12 +336,13 @@ class LoginController extends BaseController auth()->login($user, false); auth()->user()->setCompany($account->default_company); + auth()->user()->setContext($account->default_company, $user->tokens()->where('company_id', $account->default_company_id)->where('is_system', true)->first()); $user->email_verified_at = now(); $user->save(); /** @var \App\Models\CompanyUser $cu */ - $cu = $this->hydrateCompanyUser($user); + $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); @@ -394,17 +360,14 @@ class LoginController extends BaseController * * Hydrates the company user for the response * - * @param User $user * @return Builder */ - private function hydrateCompanyUser($user): Builder + private function hydrateCompanyUser(): Builder { - // /** @var \App\Models\User $user */ - // $user = auth()->user(); + /** @var \App\Models\User $user */ + $user = auth()->user(); - MultiDB::hasUser(['email' => $user->email]); - /** @var Builder $cu */ $cu = CompanyUser::query()->where('user_id', $user->id); @@ -422,10 +385,10 @@ class LoginController extends BaseController $this->setLoginCache($user); - $truth = app()->make(TruthSource::class); - $truth->setCompanyUser($cu->first()); - $truth->setUser($user); - $truth->setCompany($set_company); + // $truth = app()->make(TruthSource::class); + // $truth->setCompanyUser($cu->first()); + // $truth->setUser($user); + // $truth->setCompany($set_company); //21-03-2024 $cu->each(function ($cu) { @@ -435,7 +398,9 @@ class LoginController extends BaseController } }); - $truth->setCompanyToken(CompanyToken::where('user_id', $user->id)->where('company_id', $set_company->id)->where('is_system', true)->first()); + // $truth->setCompanyToken(CompanyToken::where('user_id', $user->id)->where('company_id', $set_company->id)->where('is_system', true)->first()); + + $user->setContext($set_company, CompanyToken::where('user_id', $user->id)->where('company_id', $set_company->id)->where('is_system', true)->first()); return CompanyUser::query()->where('user_id', $user->id); } @@ -519,10 +484,12 @@ class LoginController extends BaseController */ private function existingOauthUser($existing_user) { - Auth::login($existing_user, false); + + auth()->login($existing_user, false); + auth()->user()->setContext($existing_user->account->default_company, $existing_user->tokens()->where('company_id', $existing_user->account->default_company->id)->where('is_system', true)->first()); /** @var \App\Models\CompanyUser $cu */ - $cu = $this->hydrateCompanyUser($existing_user); + $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); @@ -535,31 +502,6 @@ class LoginController extends BaseController return $this->timeConstrainedResponse($cu); } - // private function existingLoginUser($oauth_user_id, $provider) - // { - - // /** @var \App\Models\User $user */ - // $user = auth()->user(); - - // $user->update([ - // 'oauth_user_id' => $oauth_user_id, - // 'oauth_provider_id' => $provider, - // ]); - - // /** @var \App\Models\CompanyUser $cu */ - // $cu = $this->hydrateCompanyUser($user); //should never hit - - // if ($cu->count() == 0) { - // return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); - // } - - // if (Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterprisePaidClient()) { - // return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); - // } - - // return $this->timeConstrainedResponse($cu); - // } - private function handleGoogleOauth() { $user = false; @@ -655,12 +597,13 @@ class LoginController extends BaseController // Auth::login($user, true); auth()->login($user, false); auth()->user()->setCompany($account->default_company); + auth()->user()->setContext($account->default_company, $user->tokens()->where('company_id', $account->default_company_id)->where('is_system', true)->first()); $user->email_verified_at = now(); $user->save(); /** @var \App\Models\CompanyUser $cu */ - $cu = $this->hydrateCompanyUser($user); + $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); diff --git a/app/Http/Middleware/TokenAuth.php b/app/Http/Middleware/TokenAuth.php index c1535d030e..ec7e39c690 100644 --- a/app/Http/Middleware/TokenAuth.php +++ b/app/Http/Middleware/TokenAuth.php @@ -64,28 +64,8 @@ class TokenAuth return response()->json($error, 403); } - /* - | - | Necessary evil here: As we are authenticating on CompanyToken, - | we need to link the company to the user manually. This allows - | us to decouple a $user and their attached companies completely. - | - */ - - $truth = app()->make(TruthSource::class); - - $truth->setCompanyUser($company_token->cu); - $truth->setUser($company_token->user); - $truth->setCompany($company_token->company); - $truth->setCompanyToken($company_token); - $truth->setPremiumHosted($company_token->account->isPremium()); - /* - | This method binds the db to the jobs created using this - | session - */ app('queue')->createPayloadUsing(function () use ($company_token) { return ['db' => $company_token->company->db]; - // return ['db' => $company_token->company->db, 'is_premium' => $company_token->account->isPremium()]; }); //user who once existed, but has been soft deleted @@ -101,7 +81,13 @@ class TokenAuth //stateless, don't remember the user. auth()->login($user, false); auth()->user()->setCompany($company_token->company); - + auth()->user()->setContext($company_token->company, $company_token); + + // Alternative: Bind context to service container for request duration + app()->instance('current.company', $company_token->company); + app()->instance('current.company_user', $company_token->cu); + app()->instance('current.company_token', $company_token); + return $next($request); } } diff --git a/app/Jobs/Account/CreateAccount.php b/app/Jobs/Account/CreateAccount.php index a90d879be0..f85936188a 100644 --- a/app/Jobs/Account/CreateAccount.php +++ b/app/Jobs/Account/CreateAccount.php @@ -51,13 +51,13 @@ class CreateAccount public function handle() { - if (config('ninja.environment') == 'selfhost' && Account::count() == 0) { - return $this->create(); - } elseif (config('ninja.environment') == 'selfhost' && Account::count() > 1) { - return response()->json(['message' => Ninja::selfHostedMessage()], 400); - } elseif (! Ninja::boot()) { - return response()->json(['message' => Ninja::parse()], 401); - } + // if (config('ninja.environment') == 'selfhost' && Account::count() == 0) { + // return $this->create(); + // } elseif (config('ninja.environment') == 'selfhost' && Account::count() > 1) { + // return response()->json(['message' => Ninja::selfHostedMessage()], 400); + // } elseif (! Ninja::boot()) { + // return response()->json(['message' => Ninja::parse()], 401); + // } return $this->create(); } @@ -115,6 +115,9 @@ class CreateAccount event(new AccountCreated($spaa9f78, $sp035a66, Ninja::eventVars())); } + //@replaces truthsource + auth()->user()->setContext($sp035a66, $sp2d97e8); + $spaa9f78->fresh(); if (Ninja::isHosted()) { diff --git a/app/Models/User.php b/app/Models/User.php index 33eddc3737..5289198228 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -191,6 +191,96 @@ class User extends Authenticatable implements MustVerifyEmail 'referral_earnings' => AsReferralEarningCollection::class, ]; +//////////////////////////////////////////////////////////////////////////////////// + private ?Company $contextCompany = null; + private ?CompanyUser $contextCompanyUser = null; + private ?CompanyToken $contextToken = null; + + // Set context explicitly + public function setContext(Company $company, ?CompanyToken $token = null): self + { + $this->contextCompany = $company; + $this->contextToken = $token; + $this->contextCompanyUser = $token?->company_user; + + return $this; + } + + // Transfer context from authenticated user to this instance + public function inheritContextFromAuth(): self + { + if (auth()->check() && auth()->user()->id === $this->id) { + $authUser = auth()->user(); + $this->contextCompany = $authUser->contextCompany; + $this->contextToken = $authUser->contextToken; + $this->contextCompanyUser = $authUser->contextCompanyUser; + } + + return $this; + } + + // Get current company with fallback chain + public function getCurrentCompany(): Company + { + // 1. Use explicit context if set + if ($this->contextCompany) { + return $this->contextCompany; + } + + // 2. Try service container binding (if available) + if (app()->bound('current.company')) { + return app('current.company'); + } + + // 3. Use token-based lookup + if ($token = $this->getCurrentToken()) { + return $token->company; + } + + // 4. Use default company + $defaultCompany = $this->companies()->first(); + if ($defaultCompany instanceof Company) { + return $defaultCompany; + } + + throw new \Exception('No Company Found for user ID: ' . $this->id); + } + + public function getCurrentCompanyUser(): ?CompanyUser + { + if ($this->contextCompanyUser) { + return $this->contextCompanyUser; + } + + + + // Try service container binding (if available) + if (app()->bound('current.company_user')) { + return app('current.company_user'); + } + + $company = $this->getCurrentCompany(); + return $this->company_users() + ->where('company_id', $company->id) + ->where('user_id', $this->id) + ->first(); + } + + public function getCurrentToken(): ?CompanyToken + { + if ($this->contextToken) { + return $this->contextToken; + } + + if ($apiToken = request()->header('X-API-TOKEN')) { + return CompanyToken::where('token', $apiToken)->first(); + } + + return $this->tokens()->first(); + } +///////////////////////////////////////////////////// + + public function name() { return $this->first_name.' '.$this->last_name; @@ -228,18 +318,19 @@ class User extends Authenticatable implements MustVerifyEmail public function token() { - $truth = app()->make(TruthSource::class); + return $this->getCurrentToken(); + // $truth = app()->make(TruthSource::class); - if ($truth->getCompanyToken()) { - return $truth->getCompanyToken(); - } + // if ($truth->getCompanyToken()) { + // return $truth->getCompanyToken(); + // } + // // if (request()->header('X-API-TOKEN')) { // if (request()->header('X-API-TOKEN')) { - if (request()->header('X-API-TOKEN')) { - return CompanyToken::with(['cu'])->where('token', request()->header('X-API-TOKEN'))->first(); - } + // return CompanyToken::with(['cu'])->where('token', request()->header('X-API-TOKEN'))->first(); + // } - return $this->tokens()->first(); + // return $this->tokens()->first(); } /** @@ -270,19 +361,20 @@ class User extends Authenticatable implements MustVerifyEmail */ public function getCompany(): ?Company { - $truth = app()->make(TruthSource::class); + return $this->getCurrentCompany(); + // $truth = app()->make(TruthSource::class); - // @phpstan-ignore-next-line - if ($this->company) { - return $this->company; - } elseif ($truth->getCompany()) { - return $truth->getCompany(); - } elseif (request()->header('X-API-TOKEN')) { - $company_token = CompanyToken::with('company')->where('token', request()->header('X-API-TOKEN'))->first(); - return $company_token->company; - } + // // @phpstan-ignore-next-line + // if ($this->company) { + // return $this->company; + // } elseif ($truth->getCompany()) { + // return $truth->getCompany(); + // } elseif (request()->header('X-API-TOKEN')) { + // $company_token = CompanyToken::with('company')->where('token', request()->header('X-API-TOKEN'))->first(); + // return $company_token->company; + // } - throw new \Exception('No Company Found'); + // throw new \Exception('No Company Found'); } public function companyIsSet(): bool @@ -307,28 +399,30 @@ class User extends Authenticatable implements MustVerifyEmail public function co_user() { - $truth = app()->make(TruthSource::class); + return $this->getCurrentCompanyUser(); + // $truth = app()->make(TruthSource::class); - if ($truth->getCompanyUser()) { - return $truth->getCompanyUser(); - } + // if ($truth->getCompanyUser()) { + // return $truth->getCompanyUser(); + // } - return $this->token()->cu; + // return $this->token()->cu; } public function company_user() { - if ($this->companyId()) { - return $this->belongsTo(CompanyUser::class)->where('company_id', $this->companyId())->withTrashed(); - } + return $this->getCurrentCompanyUser(); + // if ($this->companyId()) { + // return $this->belongsTo(CompanyUser::class)->where('company_id', $this->companyId())->withTrashed(); + // } - $truth = app()->make(TruthSource::class); + // $truth = app()->make(TruthSource::class); - if ($truth->getCompanyUser()) { - return $truth->getCompanyUser(); - } + // if ($truth->getCompanyUser()) { + // return $truth->getCompanyUser(); + // } - return $this->token()->cu; + // return $this->token()->cu; } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c5da5354db..df982650b2 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -82,16 +82,10 @@ class AppServiceProvider extends ServiceProvider }); /* Ensure we don't have stale state in jobs */ - Queue::before(function (JobProcessing $event) { - App::forgetInstance(TruthSource::class); - }); + // Queue::before(function (JobProcessing $event) { + // App::forgetInstance(TruthSource::class); + // }); - /* Always init a new instance everytime the container boots */ - - // app()->instance(TruthSource::class, new TruthSource()); - - - /* Extension for custom mailers */ Mail::extend('gmail', function () { diff --git a/bootstrap/app.php b/bootstrap/app.php index bf7543614f..037e17df03 100755 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -41,16 +41,6 @@ $app->singleton( App\Exceptions\Handler::class ); -/* -|-------------------------------------------------------------------------- -| Early TruthSource Binding -|-------------------------------------------------------------------------- -| Bind TruthSource early to prevent issues with early access -*/ -$app->bind(\App\Utils\TruthSource::class, function () { - return new \App\Utils\TruthSource(); -}); - /* |-------------------------------------------------------------------------- | Return The Application diff --git a/tests/Feature/Import/CSV/CsvImportTest.php b/tests/Feature/Import/CSV/CsvImportTest.php index 7ec6269d07..1180449c22 100644 --- a/tests/Feature/Import/CSV/CsvImportTest.php +++ b/tests/Feature/Import/CSV/CsvImportTest.php @@ -119,10 +119,12 @@ class CsvImportTest extends TestCase Cache::put($hash.'-recurring_invoice', base64_encode($csv), 360); - $truth = app()->make(TruthSource::class); - $truth->setCompanyUser($this->cu); - $truth->setUser($this->user); - $truth->setCompany($this->company); + $this->user->setContext($this->company, $this->token); + + // $truth = app()->make(TruthSource::class); + // $truth->setCompanyUser($this->cu); + // $truth->setUser($this->user); + // $truth->setCompany($this->company); $csv_importer = new Csv($data, $this->company); @@ -355,10 +357,12 @@ class CsvImportTest extends TestCase Cache::put($hash.'-invoice', base64_encode($csv), 360); - $truth = app()->make(TruthSource::class); - $truth->setCompanyUser($this->cu); - $truth->setUser($this->user); - $truth->setCompany($this->company); + $this->user->setContext($this->company, $this->token); + + // $truth = app()->make(TruthSource::class); + // $truth->setCompanyUser($this->cu); + // $truth->setUser($this->user); + // $truth->setCompany($this->company); $csv_importer = new Csv($data, $this->company); diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 867505d598..ebb866c349 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -295,10 +295,12 @@ trait MockAccountData $company_token->save(); - $truth = app()->make(TruthSource::class); - $truth->setCompanyUser($company_token->first()); - $truth->setUser($this->user); - $truth->setCompany($this->company); + $user->setContext($this->company, $company_token); + + // $truth = app()->make(TruthSource::class); + // $truth->setCompanyUser($company_token->first()); + // $truth->setUser($this->user); + // $truth->setCompany($this->company); //todo create one token with token name TOKEN - use firstOrCreate