diff --git a/composer.json b/composer.json index 339984a..d4fb4b1 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,6 @@ "fleetbase/twilio": "^5.0.1", "aws/aws-sdk-php-laravel": "^3.7", "fleetbase/laravel-mysql-spatial": "^1.0.2", - "genealabs/laravel-model-caching": "dev-fix/handle-datetime-values", "giggsey/libphonenumber-for-php": "^8.13", "guzzlehttp/guzzle": "^7.4", "hammerstone/fast-paginate": "^1.0", @@ -60,12 +59,6 @@ "phpstan/phpstan": "^1.10.38", "symfony/var-dumper": "^5.4.29" }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/fleetbase/laravel-model-caching" - } - ], "autoload": { "psr-4": { "Fleetbase\\": "src/", diff --git a/config/laravel-model-caching.php b/config/laravel-model-caching.php deleted file mode 100644 index 3856f16..0000000 --- a/config/laravel-model-caching.php +++ /dev/null @@ -1,11 +0,0 @@ - 'fleetbase-model-cache', - - 'enabled' => env('MODEL_CACHE_ENABLED', true), - - 'use-database-keying' => env('MODEL_CACHE_USE_DATABASE_KEYING', true), - - 'store' => env('MODEL_CACHE_STORE'), -]; \ No newline at end of file diff --git a/src/Http/Controllers/Internal/v1/AuthController.php b/src/Http/Controllers/Internal/v1/AuthController.php index 9d4b460..e2402c5 100644 --- a/src/Http/Controllers/Internal/v1/AuthController.php +++ b/src/Http/Controllers/Internal/v1/AuthController.php @@ -4,6 +4,7 @@ use Fleetbase\Exceptions\InvalidVerificationCodeException; use Fleetbase\Http\Controllers\Controller; +use Fleetbase\Http\Requests\AdminRequest; use Fleetbase\Http\Requests\Internal\ResetPasswordRequest; use Fleetbase\Http\Requests\Internal\UserForgotPasswordRequest; use Fleetbase\Http\Requests\JoinOrganizationRequest; @@ -104,7 +105,12 @@ public function session(Request $request) return response()->error('Session has expired.', 401, ['restore' => false]); } - return response()->json(['token' => $request->bearerToken(), 'user' => $user->uuid, 'verified' => $user->isVerified(), 'type' => $user->getType()]); + $session = ['token' => $request->bearerToken(), 'user' => $user->uuid, 'verified' => $user->isVerified(), 'type' => $user->getType()]; + if (session()->has('impersonator')) { + $session['impersonator'] = session()->get('impersonator'); + } + + return response()->json($session); } /** @@ -676,4 +682,69 @@ public function changeUserPassword(Request $request) return response()->json(['status' => 'ok']); } + + /** + * Allows system admin to impersonate a user. + * + * @return \Illuminate\Http\Response + */ + public function impersonate(AdminRequest $request) + { + $currentUser = Auth::getUserFromSession($request); + if ($currentUser->isNotAdmin()) { + return response()->error('Not authorized to impersonate users.'); + } + + $targetUserId = $request->input('user'); + if (!$targetUserId) { + return response()->error('Not target user selected to impersonate.'); + } + + $targetUser = User::where('uuid', $targetUserId)->first(); + if (!$targetUser) { + return response()->error('The selected user to impersonate was not found.'); + } + + try { + Auth::setSession($targetUser); + session()->put('impersonator', $currentUser->uuid); + $token = $targetUser->createToken($targetUser->uuid); + } catch (\Exception $e) { + return response()->error($e->getMessage()); + } + + return response()->json(['status' => 'ok', 'token' => $token->plainTextToken]); + } + + /** + * Ends the impersonation session. + * + * @return \Illuminate\Http\Response + */ + public function endImpersonation() + { + $impersonatorId = session()->get('impersonator'); + if (!$impersonatorId) { + return response()->error('Not impersonator session found.'); + } + + $impersonator = User::where('uuid', $impersonatorId)->first(); + if (!$impersonator) { + return response()->error('The impersonator user was not found.'); + } + + if ($impersonator->isNotAdmin()) { + return response()->error('The impersonator does not have permissions. Logout.'); + } + + try { + Auth::setSession($impersonator); + session()->remove('impersonator'); + $token = $impersonator->createToken($impersonator->uuid); + } catch (\Exception $e) { + return response()->error($e->getMessage()); + } + + return response()->json(['status' => 'ok', 'token' => $token->plainTextToken]); + } } diff --git a/src/Http/Controllers/Internal/v1/CustomFieldValueController.php b/src/Http/Controllers/Internal/v1/CustomFieldValueController.php new file mode 100644 index 0000000..d12690d --- /dev/null +++ b/src/Http/Controllers/Internal/v1/CustomFieldValueController.php @@ -0,0 +1,15 @@ + $mailer, @@ -226,6 +231,22 @@ public function getMailConfig(AdminRequest $request) $config['smtp' . ucfirst($key)] = $value; } + foreach ($mailgunConfig as $key => $value) { + $config['mailgun' . ucfirst($key)] = $value; + } + + foreach ($postmarkConfig as $key => $value) { + $config['postmark' . ucfirst($key)] = $value; + } + + foreach ($sendgridConfig as $key => $value) { + $config['sendgrid' . ucfirst($key)] = $value; + } + + foreach ($resendConfig as $key => $value) { + $config['resend' . ucfirst($key)] = $value; + } + return response()->json($config); } @@ -236,13 +257,21 @@ public function getMailConfig(AdminRequest $request) */ public function saveMailConfig(AdminRequest $request) { - $mailer = $request->input('mailer', 'smtp'); - $from = $request->input('from', []); - $smtp = $request->input('smtp', []); + $mailer = $request->input('mailer', 'smtp'); + $from = $request->array('from', []); + $smtp = $request->array('smtp'); + $mailgun = $request->array('mailgun'); + $postmark = $request->array('postmark'); + $sendgrid = $request->array('sendgrid'); + $resend = $request->array('resend'); Setting::configureSystem('mail.mailer', $mailer); Setting::configureSystem('mail.from', $from); Setting::configureSystem('mail.smtp', array_merge(['transport' => 'smtp'], $smtp)); + Setting::configureSystem('services.mailgun', $mailgun); + Setting::configureSystem('services.postmark', $postmark); + Setting::configureSystem('services.sendgrid', $sendgrid); + Setting::configureSystem('services.resend', $resend); return response()->json(['status' => 'OK']); } @@ -268,16 +297,37 @@ public function testMailConfig(AdminRequest $request) 'name' => 'Fleetbase', ] ); - $smtp = $request->input('smtp', []); - $user = $request->user(); - $message = 'Mail configuration is successful, check your inbox for the test email to confirm.'; - $status = 'success'; + $smtp = $request->input('smtp', []); + $mailgun = $request->array('mailgun'); + $postmark = $request->array('postmark'); + $sendgrid = $request->array('sendgrid'); + $resend = $request->array('resend'); + $user = $request->user(); + $message = 'Mail configuration is successful, check your inbox for the test email to confirm.'; + $status = 'success'; // set config values from input config(['mail.default' => $mailer, 'mail.from' => $from, 'mail.mailers.smtp' => array_merge(['transport' => 'smtp'], $smtp)]); + // set mailer configs + if ($mailer === 'mailgun') { + config(['services.mailgun' => $mailgun]); + } + + if ($mailer === 'postmark') { + config(['services.postmark' => $postmark]); + } + + if ($mailer === 'sendgrid') { + config(['services.sendgrid' => $sendgrid]); + } + + if ($mailer === 'resend') { + config(['services.resend' => $resend]); + } + try { - Mail::send(new \Fleetbase\Mail\TestMail($user)); + Mail::send(new \Fleetbase\Mail\TestMail($user, $mailer)); } catch (\Aws\Ses\Exception\SesException|\Exception $e) { $message = $e->getMessage(); $status = 'error'; diff --git a/src/Http/Requests/Internal/BulkActionRequest.php b/src/Http/Requests/Internal/BulkActionRequest.php new file mode 100644 index 0000000..d92e0ce --- /dev/null +++ b/src/Http/Requests/Internal/BulkActionRequest.php @@ -0,0 +1,43 @@ +session()->has('user'); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'ids' => ['required', 'array'], + ]; + } + + /** + * Get the validation rules error messages. + * + * @return array + */ + public function messages() + { + return [ + 'ids.required' => 'Please provide a resource ID.', + 'ids.array' => 'Please provide multiple resource ID\'s.', + ]; + } +} diff --git a/src/Mail/TestMail.php b/src/Mail/TestMail.php index 3cd0d6e..d33730f 100644 --- a/src/Mail/TestMail.php +++ b/src/Mail/TestMail.php @@ -26,12 +26,18 @@ class TestMail extends Mailable implements ShouldQueue */ public User $user; + /** + * The mailer used to send the email. + */ + public string $sendingMailer; + /** * Creates an instance of the TestMail. */ - public function __construct(User $user) + public function __construct(User $user, string $sendingMailer = 'smtp') { - $this->user = $user; + $this->user = $user; + $this->sendingMailer = $sendingMailer; } /** @@ -54,6 +60,7 @@ public function content(): Content markdown: 'fleetbase::mail.test', with: [ 'user' => $this->user, + 'mailer' => $this->sendingMailer, 'currentHour' => now()->hour, ] ); diff --git a/src/Models/Model.php b/src/Models/Model.php index 1ad6cf5..e0560ee 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -8,7 +8,6 @@ use Fleetbase\Traits\HasCacheableAttributes; use Fleetbase\Traits\Insertable; use Fleetbase\Traits\Searchable; -use GeneaLabs\LaravelModelCaching\Traits\Cachable; use Illuminate\Database\Eloquent\Model as EloquentModel; use Illuminate\Database\Eloquent\SoftDeletes; @@ -19,9 +18,7 @@ class Model extends EloquentModel use ClearsHttpCache; use Insertable; use Filterable; - use Expandable, Cachable { - Expandable::__call insteadof Cachable; - } + use Expandable; /** * Create a new instance of the model. diff --git a/src/Providers/CoreServiceProvider.php b/src/Providers/CoreServiceProvider.php index 64ee102..c1fef01 100644 --- a/src/Providers/CoreServiceProvider.php +++ b/src/Providers/CoreServiceProvider.php @@ -106,7 +106,6 @@ public function register() $this->mergeConfigFrom(__DIR__ . '/../../config/excel.php', 'excel'); $this->mergeConfigFrom(__DIR__ . '/../../config/sentry.php', 'sentry'); $this->mergeConfigFrom(__DIR__ . '/../../config/laravel-mysql-s3-backup.php', 'laravel-mysql-s3-backup'); - $this->mergeConfigFrom(__DIR__ . '/../../config/laravel-model-caching.php', 'laravel-model-caching'); $this->mergeConfigFrom(__DIR__ . '/../../config/responsecache.php', 'responsecache'); } diff --git a/src/Support/EnvironmentMapper.php b/src/Support/EnvironmentMapper.php index af4b664..0615840 100644 --- a/src/Support/EnvironmentMapper.php +++ b/src/Support/EnvironmentMapper.php @@ -46,6 +46,12 @@ class EnvironmentMapper 'GOOGLE_CLOUD_STORAGE_API_URI' => 'filesystem.gcs.storage_api_uri', 'SENTRY_DSN' => 'services.sentry.dsn', 'IPINFO_API_KEY' => 'services.ipinfo.api_key', + 'MAILGUN_DOMAIN' => 'services.mailgun.domain', + 'MAILGUN_SECRET' => 'services.mailgun.secret', + 'MAILGUN_ENDPOINT' => 'services.mailgun.endpoint', + 'POSTMARK_TOKEN' => 'services.postmark.token', + 'SENDGRID_API_KEY' => 'services.sendgrid.api_key', + 'RESEND_KEY' => 'services.resend.key', ]; /** @@ -86,6 +92,10 @@ class EnvironmentMapper ['settingsKey' => 'services.twilio', 'configKey' => 'twilio.twilio.connections.twilio'], ['settingsKey' => 'services.ipinfo', 'configKey' => 'services.ipinfo'], ['settingsKey' => 'services.ipinfo', 'configKey' => 'fleetbase.services.ipinfo'], + ['settingsKey' => 'services.mailgun', 'configKey' => 'services.mailgun'], + ['settingsKey' => 'services.postmark', 'configKey' => 'services.postmark'], + ['settingsKey' => 'services.sendgrid', 'configKey' => 'services.sendgrid'], + ['settingsKey' => 'services.resend', 'configKey' => 'services.resend'], ['settingsKey' => 'services.sentry.dsn', 'configKey' => 'sentry.dsn'], ['settingsKey' => 'broadcasting.apn', 'configKey' => 'broadcasting.connections.apn'], ['settingsKey' => 'firebase.app', 'configKey' => 'firebase.projects.app'], diff --git a/src/Support/Http.php b/src/Support/Http.php index 8e3e7c5..d9b5d00 100644 --- a/src/Support/Http.php +++ b/src/Support/Http.php @@ -137,7 +137,7 @@ public static function lookupIp($ip = null) public static function action(?string $verb = null) { - $verb = $verb ?? $_SERVER['REQUEST_METHOD']; + $verb = $verb ? $verb : (isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null); $action = Str::lower($verb); switch ($verb) { diff --git a/src/routes.php b/src/routes.php index 4115471..53e8f4e 100644 --- a/src/routes.php +++ b/src/routes.php @@ -144,6 +144,8 @@ function ($router) { ['prefix' => 'auth'], function ($router) { $router->post('change-user-password', 'AuthController@changeUserPassword'); + $router->post('impersonate', 'AuthController@impersonate'); + $router->delete('impersonate', 'AuthController@endImpersonation'); } ); $router->fleetbaseRoutes( diff --git a/views/mail/test.blade.php b/views/mail/test.blade.php index 089c2eb..53b3d72 100644 --- a/views/mail/test.blade.php +++ b/views/mail/test.blade.php @@ -9,5 +9,7 @@ @endif -🎉 This is a test email from Fleetbase to confirm that your mail configuration works. +

🎉 This is a test email from Fleetbase to confirm that your mail configuration works.

+

Test Email sent using Mailer: {{ Str::title($mailer) }}

+

Environment: {{ app()->environment() }}

\ No newline at end of file