From bd3d9eb54f452f82eba73730ca869436e33b1cb3 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Fri, 7 Jun 2024 01:13:06 +0200 Subject: [PATCH 1/5] Add support to publicly availability for users logged in Twill --- .../TwillFeatureFlagController.php | 6 ++++ src/Models/TwillFeatureFlag.php | 8 ++++- .../TwillFeatureFlagRepository.php | 30 +++++++++++++++---- src/ServiceProvider.php | 3 ++ src/Support/Facades/TwillHttpBasicAuth.php | 14 +++++++++ ..._114937_add_public_available_for_twill.php | 28 +++++++++++++++++ src/resources/views/admin/form.blade.php | 5 ++++ 7 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 src/Support/Facades/TwillHttpBasicAuth.php create mode 100644 src/database/migrations/2024_06_04_114937_add_public_available_for_twill.php diff --git a/src/Http/Controllers/TwillFeatureFlagController.php b/src/Http/Controllers/TwillFeatureFlagController.php index 0a7fbf0..7c906b5 100644 --- a/src/Http/Controllers/TwillFeatureFlagController.php +++ b/src/Http/Controllers/TwillFeatureFlagController.php @@ -27,6 +27,12 @@ class TwillFeatureFlagController extends ModuleController 'sort' => true, ], + 'publicly_available_twill_users' => [ + 'title' => 'Publicly available for Twill users', + 'field' => 'publicly_available_twill_users_yes_no', + 'sort' => true, + ], + 'publicly_available_ips' => [ 'title' => 'Publicly available to (IPs)', 'field' => 'publicly_available_ips', diff --git a/src/Models/TwillFeatureFlag.php b/src/Models/TwillFeatureFlag.php index 1ef27a2..8cb9a42 100644 --- a/src/Models/TwillFeatureFlag.php +++ b/src/Models/TwillFeatureFlag.php @@ -14,12 +14,13 @@ * @property string $publicly_available_yes_no * @property string|null $publicly_available_ips * @property bool $published + * @property bool $publicly_available_twill_users */ class TwillFeatureFlag extends Model { use HasRevisions; - protected $fillable = ['published', 'title', 'description', 'code', 'publicly_available', 'ip_addresses']; + protected $fillable = ['published', 'title', 'description', 'code', 'publicly_available', 'ip_addresses', 'publicly_available_twill_users']; /** * Save the model to the database. @@ -39,6 +40,11 @@ public function getPubliclyAvailableYesNoAttribute(): string return $this->publicly_available ? 'Yes' : ''; } + public function getPubliclyAvailableTwillUsersYesNoAttribute(): string + { + return $this->publicly_available_twill_users ? 'Yes' : ''; + } + public function getPubliclyAvailableIpsAttribute(): string|null { return $this->ip_addresses ?? null; diff --git a/src/Repositories/TwillFeatureFlagRepository.php b/src/Repositories/TwillFeatureFlagRepository.php index a5c2e18..41caf6d 100644 --- a/src/Repositories/TwillFeatureFlagRepository.php +++ b/src/Repositories/TwillFeatureFlagRepository.php @@ -73,11 +73,8 @@ public function featureList(bool $all = false): array private function isPubliclyAvailableToCurrentUser(TwillFeatureFlag $featureFlag): bool { - return (new GeolocationService())->currentIpAddressIsOnList( - collect(explode(',', $featureFlag->ip_addresses)) - ->map(fn($ip) => trim($ip)) - ->toArray(), - ); + return $this->isPubliclyAvailableToIpAddresses($featureFlag) || + $this->isPubliclyAvailableToTwillUsers($featureFlag); } private function bootCache(): void @@ -132,4 +129,27 @@ protected function url(array|bool $parsed, string $attribute): string|null return $parsed[$attribute]; } + + public function isPubliclyAvailableToIpAddresses(TwillFeatureFlag $featureFlag): bool + { + if (blank($featureFlag->ip_addresses)) { + return false; + } + + return (new GeolocationService())->currentIpAddressIsOnList( + collect(explode(',', $featureFlag->ip_addresses)) + ->map(fn($ip) => trim($ip)) + ->toArray(), + ); + } + + public function isPubliclyAvailableToTwillUsers(TwillFeatureFlag $featureFlag): bool + { + if (!$featureFlag->publicly_available_twill_users) { + return false; + } + + // Is the user authenticated on Twill? + return auth('twill_users')->check(); + } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 672b137..587e8f7 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -5,6 +5,7 @@ use Illuminate\Support\Str; use A17\Twill\Facades\TwillCapsules; use A17\Twill\TwillPackageServiceProvider; +use A17\TwillFeatureFlags\Support\TwillFeatureFlags; class ServiceProvider extends TwillPackageServiceProvider { @@ -27,5 +28,7 @@ protected function registerThisCapsule(): void $namespace, $this->getPackageDirectory() . '/src', ); + + app()->singleton(TwillFeatureFlags::class, fn() => new TwillFeatureFlags()); } } diff --git a/src/Support/Facades/TwillHttpBasicAuth.php b/src/Support/Facades/TwillHttpBasicAuth.php new file mode 100644 index 0000000..06f8a90 --- /dev/null +++ b/src/Support/Facades/TwillHttpBasicAuth.php @@ -0,0 +1,14 @@ +boolean('publicly_available_twill_users')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('twill_feature_flags', function (Blueprint $table) { + $table->dropColumn('publicly_available_twill_users'); + }); + } +}; diff --git a/src/resources/views/admin/form.blade.php b/src/resources/views/admin/form.blade.php index 0578ab9..6693b8b 100644 --- a/src/resources/views/admin/form.blade.php +++ b/src/resources/views/admin/form.blade.php @@ -12,6 +12,11 @@ 'label' => 'Publicly available', ]) + @formField('checkbox', [ + 'name' => 'publicly_available_twill_users', + 'label' => 'Publicly available for users logged in Twill', + ]) + @formField('input', [ 'label' => 'IP Addresses', 'name' => 'ip_addresses', From 3a2d8876c5fad668de2e76a423b22dd202d2f713 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Fri, 7 Jun 2024 01:20:05 +0200 Subject: [PATCH 2/5] Disable option if SESSION_DOMAIN is blank and document the feature --- README.md | 9 +++++++++ src/resources/views/admin/form.blade.php | 14 ++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 03fd77b..ba42e24 100644 --- a/README.md +++ b/README.md @@ -87,3 +87,12 @@ Or in Blade: ``` Don't forget to add the feature flags to your navigation too. + +## Allow users logged in Twill option +Your sessions must use a shared domain, otherwise your frontend will not have access to the Laravel session on the Twill and the option will be disabled: + +```dotenv +SESSION_DOMAIN=.laravel-twill-project.test +``` + +Make sure your sessions are working fine, when you switch to a shared domain they might break and a browser cookie clear might be needed. diff --git a/src/resources/views/admin/form.blade.php b/src/resources/views/admin/form.blade.php index 6693b8b..241d316 100644 --- a/src/resources/views/admin/form.blade.php +++ b/src/resources/views/admin/form.blade.php @@ -1,5 +1,9 @@ @extends('twill::layouts.form') +@php + $allowUsersInTwill = filled(config('session_domain')); +@endphp + @section('contentFields') @formField('input', [ 'label' => 'Code', @@ -12,10 +16,12 @@ 'label' => 'Publicly available', ]) - @formField('checkbox', [ - 'name' => 'publicly_available_twill_users', - 'label' => 'Publicly available for users logged in Twill', - ]) + @if($allowUsersInTwill) + @formField('checkbox', [ + 'name' => 'publicly_available_twill_users', + 'label' => 'Publicly available for users logged in Twill', + ]) + @endif @formField('input', [ 'label' => 'IP Addresses', From 5482f0df013e303477f73305a27d7a8908bed1b1 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Fri, 7 Jun 2024 10:47:37 +0200 Subject: [PATCH 3/5] Improve domain and session checking and document it better --- README.md | 7 +++++-- src/ServiceProvider.php | 3 +++ src/Services/Helpers.php | 2 +- src/Support/helpers.php | 24 ++++++++++++++++++++++++ src/resources/views/admin/form.blade.php | 6 +----- 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ba42e24..a27b832 100644 --- a/README.md +++ b/README.md @@ -89,10 +89,13 @@ Or in Blade: Don't forget to add the feature flags to your navigation too. ## Allow users logged in Twill option -Your sessions must use a shared domain, otherwise your frontend will not have access to the Laravel session on the Twill and the option will be disabled: +You can allow your users to have access to a feature in public available domains if they are logged in on Twill. But there are some caveats for it to work: + +- Twill must be int he same domain of the web application, meaning that ADMIN_APP_URL must be empty or have the same domain as the frontend; or +- The Laravel session domain should set in order for the apps to share the session cookie: ```dotenv SESSION_DOMAIN=.laravel-twill-project.test ``` -Make sure your sessions are working fine, when you switch to a shared domain they might break and a browser cookie clear might be needed. +Also, make sure your sessions are working fine, when you switch to a shared domain they might break and a browser cookie clear might be needed. diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 587e8f7..c91ef26 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -5,6 +5,7 @@ use Illuminate\Support\Str; use A17\Twill\Facades\TwillCapsules; use A17\Twill\TwillPackageServiceProvider; +use A17\TwillFeatureFlags\Services\Helpers; use A17\TwillFeatureFlags\Support\TwillFeatureFlags; class ServiceProvider extends TwillPackageServiceProvider @@ -16,6 +17,8 @@ public function boot(): void { $this->registerThisCapsule(); + Helpers::load(); + parent::boot(); } diff --git a/src/Services/Helpers.php b/src/Services/Helpers.php index 82524e1..2ece564 100644 --- a/src/Services/Helpers.php +++ b/src/Services/Helpers.php @@ -6,6 +6,6 @@ class Helpers { public static function load(): void { - require __DIR__ . '/../Support/helpers.php'; + require_once __DIR__ . '/../Support/helpers.php'; } } diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 4825e5a..41ed863 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -15,3 +15,27 @@ function feature_list(bool $all = false): array return (new TwillFeatureFlagRepository())->featureList($all); } } + +if (!function_exists('features_can_be_public_on_twill')) { + function features_can_be_public_on_twill(): bool + { + $sessionDomain = config('session.domain'); + + $twillAdminAppHost = parse_url(config('twill.admin_app_url') ?? '')['host'] ?? null; + + $appDomainHost = parse_url(config('app.url') ?? '')['host'] ?? null; + + // If the admin app is not set it probably means Twill is in the same domain of the frontend + if (blank($twillAdminAppHost)) { + return true; + } + + // Otherwise the domain must be the same + if ($twillAdminAppHost === $appDomainHost) { + return true; + } + + // Otherwise the session domain must be set + return filled($sessionDomain); + } +} diff --git a/src/resources/views/admin/form.blade.php b/src/resources/views/admin/form.blade.php index 241d316..b3551fd 100644 --- a/src/resources/views/admin/form.blade.php +++ b/src/resources/views/admin/form.blade.php @@ -1,9 +1,5 @@ @extends('twill::layouts.form') -@php - $allowUsersInTwill = filled(config('session_domain')); -@endphp - @section('contentFields') @formField('input', [ 'label' => 'Code', @@ -16,7 +12,7 @@ 'label' => 'Publicly available', ]) - @if($allowUsersInTwill) + @if(features_can_be_public_on_twill()) @formField('checkbox', [ 'name' => 'publicly_available_twill_users', 'label' => 'Publicly available for users logged in Twill', From 70cd013559cca8f9857aeb40f72ac89dea08e31a Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Fri, 7 Jun 2024 10:49:58 +0200 Subject: [PATCH 4/5] Format it better --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a27b832..0aec326 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Don't forget to add the feature flags to your navigation too. ## Allow users logged in Twill option You can allow your users to have access to a feature in public available domains if they are logged in on Twill. But there are some caveats for it to work: -- Twill must be int he same domain of the web application, meaning that ADMIN_APP_URL must be empty or have the same domain as the frontend; or +- Twill must be int he same domain of the web application, meaning that `ADMIN_APP_URL` must be empty or have the same domain as the frontend; or - The Laravel session domain should set in order for the apps to share the session cookie: ```dotenv From ab8b4a0313ddcca518b31bbcf7d0d601bb2d4609 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Fri, 7 Jun 2024 11:00:54 +0200 Subject: [PATCH 5/5] Fix all PHPStan level 8 errors --- phpstan.neon | 6 ++---- src/Repositories/TwillFeatureFlagRepository.php | 4 ++-- src/ServiceProvider.php | 3 --- src/Support/Facades/TwillHttpBasicAuth.php | 14 -------------- 4 files changed, 4 insertions(+), 23 deletions(-) delete mode 100644 src/Support/Facades/TwillHttpBasicAuth.php diff --git a/phpstan.neon b/phpstan.neon index 964a330..0392d53 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,12 +9,10 @@ parameters: excludePaths: - checkMissingIterableValueType: false - universalObjectCratesClasses: ignoreErrors: + - identifier: missingType.generics + - identifier: missingType.iterableValue reportUnmatchedIgnoredErrors: false - - checkGenericClassInNonGenericObjectType: false diff --git a/src/Repositories/TwillFeatureFlagRepository.php b/src/Repositories/TwillFeatureFlagRepository.php index 41caf6d..7c8a207 100644 --- a/src/Repositories/TwillFeatureFlagRepository.php +++ b/src/Repositories/TwillFeatureFlagRepository.php @@ -40,7 +40,7 @@ public function getFeature(string $code): bool return false; } - if (blank($featureFlag) || blank($featureFlag?->published) || $featureFlag?->published === false) { + if (blank($featureFlag) || blank($featureFlag->published) || $featureFlag->published === false) { return false; } @@ -74,7 +74,7 @@ public function featureList(bool $all = false): array private function isPubliclyAvailableToCurrentUser(TwillFeatureFlag $featureFlag): bool { return $this->isPubliclyAvailableToIpAddresses($featureFlag) || - $this->isPubliclyAvailableToTwillUsers($featureFlag); + $this->isPubliclyAvailableToTwillUsers($featureFlag); } private function bootCache(): void diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index c91ef26..1b4e421 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -6,7 +6,6 @@ use A17\Twill\Facades\TwillCapsules; use A17\Twill\TwillPackageServiceProvider; use A17\TwillFeatureFlags\Services\Helpers; -use A17\TwillFeatureFlags\Support\TwillFeatureFlags; class ServiceProvider extends TwillPackageServiceProvider { @@ -31,7 +30,5 @@ protected function registerThisCapsule(): void $namespace, $this->getPackageDirectory() . '/src', ); - - app()->singleton(TwillFeatureFlags::class, fn() => new TwillFeatureFlags()); } } diff --git a/src/Support/Facades/TwillHttpBasicAuth.php b/src/Support/Facades/TwillHttpBasicAuth.php deleted file mode 100644 index 06f8a90..0000000 --- a/src/Support/Facades/TwillHttpBasicAuth.php +++ /dev/null @@ -1,14 +0,0 @@ -